@@ -10,6 +10,9 @@ use std::sync::Arc;
1010
1111use crate :: AppState ;
1212
13+ // Import BitCell types
14+ use bitcell_ca:: { Battle , Glider , GliderPattern , Position , BattleOutcome } ;
15+
1316#[ derive( Debug , Deserialize ) ]
1417pub struct RunBattleTestRequest {
1518 pub glider_a : String ,
@@ -27,6 +30,33 @@ pub struct BattleTestResponse {
2730 pub duration_ms : u64 ,
2831}
2932
33+ #[ derive( Debug , Deserialize ) ]
34+ pub struct BattleVisualizationRequest {
35+ pub glider_a : String ,
36+ pub glider_b : String ,
37+ pub steps : Option < usize > ,
38+ pub frame_count : Option < usize > ,
39+ pub downsample_size : Option < usize > ,
40+ }
41+
42+ #[ derive( Debug , Serialize ) ]
43+ pub struct BattleVisualizationResponse {
44+ pub test_id : String ,
45+ pub winner : String ,
46+ pub steps : usize ,
47+ pub final_energy_a : u64 ,
48+ pub final_energy_b : u64 ,
49+ pub frames : Vec < BattleFrame > ,
50+ }
51+
52+ #[ derive( Debug , Serialize ) ]
53+ pub struct BattleFrame {
54+ pub step : usize ,
55+ pub grid : Vec < Vec < u8 > > ,
56+ pub energy_a : u64 ,
57+ pub energy_b : u64 ,
58+ }
59+
3060#[ derive( Debug , Deserialize ) ]
3161pub struct SendTestTransactionRequest {
3262 pub from : Option < String > ,
@@ -41,23 +71,85 @@ pub struct TransactionTestResponse {
4171 pub message : String ,
4272}
4373
74+ fn parse_glider_pattern ( name : & str ) -> Result < GliderPattern , String > {
75+ match name. to_lowercase ( ) . as_str ( ) {
76+ "standard" => Ok ( GliderPattern :: Standard ) ,
77+ "lightweight" | "lwss" => Ok ( GliderPattern :: Lightweight ) ,
78+ "middleweight" | "mwss" => Ok ( GliderPattern :: Middleweight ) ,
79+ "heavyweight" | "hwss" => Ok ( GliderPattern :: Heavyweight ) ,
80+ _ => Err ( format ! ( "Unknown glider pattern: {}" , name) ) ,
81+ }
82+ }
83+
4484/// Run a battle test
4585pub async fn run_battle_test (
4686 State ( _state) : State < Arc < AppState > > ,
4787 Json ( req) : Json < RunBattleTestRequest > ,
4888) -> Result < Json < BattleTestResponse > , ( StatusCode , Json < String > ) > {
49- // TODO: Actually run battle simulation
50- // For now, return mock response
51-
5289 let test_id = format ! ( "test-{}" , chrono:: Utc :: now( ) . timestamp( ) ) ;
5390
91+ tracing:: info!( "Running battle test: {} vs {}" , req. glider_a, req. glider_b) ;
92+
93+ // Parse glider patterns
94+ let pattern_a = parse_glider_pattern ( & req. glider_a )
95+ . map_err ( |e| ( StatusCode :: BAD_REQUEST , Json ( e) ) ) ?;
96+
97+ let pattern_b = parse_glider_pattern ( & req. glider_b )
98+ . map_err ( |e| ( StatusCode :: BAD_REQUEST , Json ( e) ) ) ?;
99+
100+ // Create gliders
101+ let glider_a = Glider :: new ( pattern_a, Position :: new ( 256 , 512 ) ) ;
102+ let glider_b = Glider :: new ( pattern_b, Position :: new ( 768 , 512 ) ) ;
103+
104+ // Create battle
105+ let steps = req. steps . unwrap_or ( 1000 ) ;
106+ let battle = if steps != 1000 {
107+ Battle :: with_steps ( glider_a, glider_b, steps)
108+ } else {
109+ Battle :: new ( glider_a, glider_b)
110+ } ;
111+
112+ // Run battle simulation
113+ let start = std:: time:: Instant :: now ( ) ;
114+
115+ let ( outcome, energy_a, energy_b) = tokio:: task:: spawn_blocking ( move || {
116+ // Simulate the battle
117+ let outcome = battle. simulate ( )
118+ . map_err ( |e| format ! ( "Battle simulation error: {:?}" , e) ) ?;
119+
120+ // Get final grid to measure energies
121+ let final_grid = battle. final_grid ( ) ;
122+ let ( energy_a, energy_b) = battle. measure_regional_energy ( & final_grid) ;
123+
124+ Ok :: < _ , String > ( ( outcome, energy_a, energy_b) )
125+ } )
126+ . await
127+ . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( format ! ( "Task join error: {}" , e) ) ) ) ?
128+ . map_err ( |e : String | ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( e) ) ) ?;
129+
130+ let duration = start. elapsed ( ) ;
131+
132+ let winner = match outcome {
133+ BattleOutcome :: AWins => "glider_a" . to_string ( ) ,
134+ BattleOutcome :: BWins => "glider_b" . to_string ( ) ,
135+ BattleOutcome :: Tie => "tie" . to_string ( ) ,
136+ } ;
137+
138+ tracing:: info!(
139+ "Battle test completed: winner={}, energy_a={}, energy_b={}, duration={}ms" ,
140+ winner,
141+ energy_a,
142+ energy_b,
143+ duration. as_millis( )
144+ ) ;
145+
54146 let response = BattleTestResponse {
55147 test_id,
56- winner : "glider_a" . to_string ( ) ,
57- steps : req . steps . unwrap_or ( 1000 ) ,
58- final_energy_a : 8500 ,
59- final_energy_b : 7200 ,
60- duration_ms : 235 ,
148+ winner,
149+ steps,
150+ final_energy_a : energy_a ,
151+ final_energy_b : energy_b ,
152+ duration_ms : duration . as_millis ( ) as u64 ,
61153 } ;
62154
63155 Ok ( Json ( response) )
@@ -68,19 +160,118 @@ pub async fn send_test_transaction(
68160 State ( _state) : State < Arc < AppState > > ,
69161 Json ( req) : Json < SendTestTransactionRequest > ,
70162) -> Result < Json < TransactionTestResponse > , ( StatusCode , Json < String > ) > {
71- // TODO: Actually send transaction
72- // For now, return mock response
163+ // TODO: Actually send transaction to a running node
164+ // For now, return a formatted response
73165
74166 let tx_hash = format ! ( "0x{:x}" , chrono:: Utc :: now( ) . timestamp( ) ) ;
75167
76168 let response = TransactionTestResponse {
77169 tx_hash,
78170 status : "pending" . to_string ( ) ,
79- message : format ! ( "Test transaction sent: {} -> {}" ,
171+ message : format ! (
172+ "Test transaction sent: {} -> {} ({} units)" ,
80173 req. from. unwrap_or_else( || "genesis" . to_string( ) ) ,
81- req. to
174+ req. to,
175+ req. amount
82176 ) ,
83177 } ;
84178
179+ tracing:: info!( "Test transaction: {}" , response. message) ;
180+
181+ Ok ( Json ( response) )
182+ }
183+
184+ /// Run a battle with visualization frames
185+ pub async fn run_battle_visualization (
186+ State ( _state) : State < Arc < AppState > > ,
187+ Json ( req) : Json < BattleVisualizationRequest > ,
188+ ) -> Result < Json < BattleVisualizationResponse > , ( StatusCode , Json < String > ) > {
189+ let test_id = format ! ( "viz-{}" , chrono:: Utc :: now( ) . timestamp( ) ) ;
190+
191+ tracing:: info!( "Running battle visualization: {} vs {}" , req. glider_a, req. glider_b) ;
192+
193+ // Parse glider patterns
194+ let pattern_a = parse_glider_pattern ( & req. glider_a )
195+ . map_err ( |e| ( StatusCode :: BAD_REQUEST , Json ( e) ) ) ?;
196+
197+ let pattern_b = parse_glider_pattern ( & req. glider_b )
198+ . map_err ( |e| ( StatusCode :: BAD_REQUEST , Json ( e) ) ) ?;
199+
200+ // Create gliders
201+ let glider_a = Glider :: new ( pattern_a, Position :: new ( 256 , 512 ) ) ;
202+ let glider_b = Glider :: new ( pattern_b, Position :: new ( 768 , 512 ) ) ;
203+
204+ // Create battle
205+ let steps = req. steps . unwrap_or ( 1000 ) ;
206+ let frame_count = req. frame_count . unwrap_or ( 20 ) . min ( 100 ) ; // Max 100 frames
207+ let downsample_size = req. downsample_size . unwrap_or ( 128 ) . min ( 512 ) ; // Max 512x512
208+
209+ let battle = if steps != 1000 {
210+ Battle :: with_steps ( glider_a, glider_b, steps)
211+ } else {
212+ Battle :: new ( glider_a, glider_b)
213+ } ;
214+
215+ // Calculate which steps to capture
216+ let sample_interval = steps / frame_count;
217+ let mut sample_steps: Vec < usize > = ( 0 ..frame_count)
218+ . map ( |i| i * sample_interval)
219+ . collect ( ) ;
220+ sample_steps. push ( steps) ; // Always include final step
221+
222+ // Run simulation and capture frames
223+ let ( outcome, frames) = tokio:: task:: spawn_blocking ( move || {
224+ // Get outcome
225+ let outcome = battle. simulate ( )
226+ . map_err ( |e| format ! ( "Battle simulation error: {:?}" , e) ) ?;
227+
228+ // Get grid states at sample steps
229+ let grids = battle. grid_states ( & sample_steps) ;
230+
231+ // Create frames with downsampled grids and energy measurements
232+ let mut frames = Vec :: new ( ) ;
233+ for ( i, grid) in grids. iter ( ) . enumerate ( ) {
234+ let step = sample_steps[ i] ;
235+ let ( energy_a, energy_b) = battle. measure_regional_energy ( grid) ;
236+ let downsampled = grid. downsample ( downsample_size) ;
237+
238+ frames. push ( BattleFrame {
239+ step,
240+ grid : downsampled,
241+ energy_a,
242+ energy_b,
243+ } ) ;
244+ }
245+
246+ Ok :: < _ , String > ( ( outcome, frames) )
247+ } )
248+ . await
249+ . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( format ! ( "Task join error: {}" , e) ) ) ) ?
250+ . map_err ( |e : String | ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( e) ) ) ?;
251+
252+ let winner = match outcome {
253+ BattleOutcome :: AWins => "glider_a" . to_string ( ) ,
254+ BattleOutcome :: BWins => "glider_b" . to_string ( ) ,
255+ BattleOutcome :: Tie => "tie" . to_string ( ) ,
256+ } ;
257+
258+ let final_energy_a = frames. last ( ) . map ( |f| f. energy_a ) . unwrap_or ( 0 ) ;
259+ let final_energy_b = frames. last ( ) . map ( |f| f. energy_b ) . unwrap_or ( 0 ) ;
260+
261+ tracing:: info!(
262+ "Battle visualization completed: winner={}, {} frames captured" ,
263+ winner,
264+ frames. len( )
265+ ) ;
266+
267+ let response = BattleVisualizationResponse {
268+ test_id,
269+ winner,
270+ steps,
271+ final_energy_a,
272+ final_energy_b,
273+ frames,
274+ } ;
275+
85276 Ok ( Json ( response) )
86277}
0 commit comments