restore first circle algo + hostsim work
[aversive.git] / projects / microb2010 / tests / hostsim / strat.c
1 /*  
2  *  Copyright Droids, Microb Technology (2009)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: strat.c,v 1.6 2009-11-08 17:24:33 zer0 Exp $
19  *
20  *  Olivier MATZ <zer0@droids-corp.org> 
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27
28 #include <aversive/pgmspace.h>
29 #include <aversive/queue.h>
30 #include <aversive/wait.h>
31 #include <aversive/error.h>
32
33 #include <uart.h>
34 #include <clock_time.h>
35
36 #include <pid.h>
37 #include <quadramp.h>
38 #include <control_system_manager.h>
39 #include <trajectory_manager.h>
40 #include <vect_base.h>
41 #include <lines.h>
42 #include <polygon.h>
43 #include <obstacle_avoidance.h>
44 #include <blocking_detection_manager.h>
45 #include <robot_system.h>
46 #include <position_manager.h>
47
48 #include <rdline.h>
49 #include <parse.h>
50
51 #include "main.h"
52 #include "strat.h"
53 #include "strat_base.h"
54 #include "strat_utils.h"
55 #include "i2c_commands.h"
56
57
58 #define COL_DISP_MARGIN 400 /* stop 40 cm in front of dispenser */
59 #define COL_SCAN_PRE_MARGIN 250
60
61
62 #ifdef TEST_BEACON
63
64 #define BEACON_MAX_SAMPLES 100
65 struct beacon_sample {
66         int16_t posx;
67         int16_t posy;
68         int16_t posa;
69         int16_t oppx;
70         int16_t oppy;
71         uint8_t time;
72 };
73
74 static struct beacon_sample beacon_sample[BEACON_MAX_SAMPLES];
75 static uint8_t beacon_prev_time = 0;
76 static uint8_t beacon_cur_idx = 0;
77
78 static void beacon_update_samples(void)
79 {
80         int16_t opp_a, opp_d, opp_x, opp_y;
81         int8_t err;
82         uint8_t time;
83
84         time = time_get_s();
85
86         /* one sample per second max */
87         if (time <= beacon_prev_time)
88                 return;
89         /* limit max number of samples */
90         if (beacon_cur_idx >= BEACON_MAX_SAMPLES)
91                 return;
92
93         memset(&beacon_sample[beacon_cur_idx], 0, sizeof(beacon_sample[beacon_cur_idx]));
94         beacon_prev_time = time;
95         beacon_sample[beacon_cur_idx].time = time;
96         
97         /* get opponent pos; if not found, just set struct to 0 */
98         err = get_opponent_xyda(&opp_x, &opp_y, &opp_d, &opp_a);
99         if (err == -1)
100                 return;
101
102         beacon_sample[beacon_cur_idx].posx = position_get_x_s16(&mainboard.pos);
103         beacon_sample[beacon_cur_idx].posy = position_get_y_s16(&mainboard.pos);
104         beacon_sample[beacon_cur_idx].posa = position_get_a_deg_s16(&mainboard.pos);
105         beacon_sample[beacon_cur_idx].oppx = opp_x;
106         beacon_sample[beacon_cur_idx].oppy = opp_y;
107         beacon_cur_idx++;
108 }
109
110 void beacon_dump_samples(void)
111 {
112         uint16_t i;
113
114         for (i=0; i<BEACON_MAX_SAMPLES; i++) {
115                 printf_P(PSTR("%d: pos=(%d,%d,%d) opp=(%d,%d) time=%d\r\n"),
116                          i,
117                          beacon_sample[i].posx,
118                          beacon_sample[i].posy,
119                          beacon_sample[i].posa,
120                          beacon_sample[i].oppx,
121                          beacon_sample[i].oppy,
122                          beacon_sample[i].time);
123         }
124 }
125 #endif
126
127 struct strat_infos strat_infos = {
128         /* conf */
129         .conf = {
130                 .flags = 0,
131                 /* scanner disabled by default */
132                 .scan_opp_min_time = 90,
133                 .delay_between_opp_scan = 90,
134                 .scan_our_min_time = 90,
135                 .delay_between_our_scan = 90,
136                 .wait_opponent = 0,
137                 .lintel_min_time = 0,
138                 .scan_opp_angle = -1,
139         },
140
141         /* static columns */
142         .s_cols = { 
143                 .flags = 0, 
144                 .configuration = 0,
145         },
146
147         /* column dispensers ; be carreful, positions are
148          * color-dependent, so COLOR_Y() and COLOR_A() should be
149          * used. All angles here are _absolute_ */
150         .c1 = {
151                 .checkpoint_x = 2711 - COL_SCAN_PRE_MARGIN,
152                 .checkpoint_y = AREA_Y - COL_DISP_MARGIN,
153                 .scan_left = 0,
154                 .scan_a = 180,
155                 .eject_a = 180,
156                 .recalib_x = 2711,
157                 .recalib_y = AREA_Y - (ROBOT_LENGTH/2 + DIST_BACK_DISPENSER),
158                 .pickup_a = 90,
159                 .name = "col_disp1",
160         },
161         .c2 = {
162                 .checkpoint_x = AREA_X - COL_DISP_MARGIN,
163                 .checkpoint_y = 800 - COL_SCAN_PRE_MARGIN,
164                 .scan_left = 1,
165                 .scan_a = -90,
166                 .eject_a = -90,
167                 .recalib_x = AREA_X - (ROBOT_LENGTH/2 + DIST_BACK_DISPENSER),
168                 .recalib_y = 800,
169                 .pickup_a = 0,
170                 .name = "col_disp2",
171         },
172         .c3 = {
173                 .checkpoint_x = AREA_X-COL_DISP_MARGIN,
174                 .checkpoint_y = 1300 + COL_SCAN_PRE_MARGIN,
175                 .scan_a = 90,
176                 .scan_left = 0,
177                 .eject_a = -90,
178                 .recalib_x = AREA_X - (ROBOT_LENGTH/2 + DIST_BACK_DISPENSER),
179                 .recalib_y = 1300,
180                 .pickup_a = 0,
181                 .name = "col_disp3",
182         },
183         
184         /* lintel dispensers */
185         .l1 = {
186                 .x = 912, /* XXX for red only */
187                 .name = "lin_disp1",
188         },
189         .l2 = {
190                 .x = 1312,  /* XXX for red only */
191                 .name = "lin_disp2",
192         },
193
194         /* build zones */
195         .zone_list = {
196 #define ZONE_DISC_NUM 0
197                 {
198                         .flags = ZONE_F_VALID | ZONE_F_DISC,
199                         .level = 2,
200                         .checkpoint_x = 0,
201                         .checkpoint_x = 0,
202                         .name = "disc",
203                 },
204 #define ZONE_1A_NUM 1
205                 {
206                         .flags = ZONE_F_VALID,
207                         .level = 1,
208                         .checkpoint_x = 1385,
209                         .checkpoint_y = 1700,
210                         .name = "z1a",
211                 },
212 #define ZONE_1B_NUM 2
213                 {
214                         .flags = ZONE_F_VALID,
215                         .level = 1,
216                         .checkpoint_x = 1615,
217                         .checkpoint_y = 1700,
218                         .name = "z1b",
219                 },
220 #define ZONE_0B_NUM 3
221                 {
222                         .flags = ZONE_F_VALID,
223                         .level = 0,
224                         .checkpoint_x = 2100,
225                         .checkpoint_y = 1700,
226                         .name = "z0b",
227                 },
228 #define ZONE_0A_NUM 4
229                 {
230                         .flags = ZONE_F_VALID,
231                         .level = 0,
232                         .checkpoint_x = 900,
233                         .checkpoint_y = 1700,
234                         .name = "z0a",
235                 },
236         }
237 };
238
239 /*************************************************************/
240
241 /*                  INIT                                     */
242
243 /*************************************************************/
244
245 void strat_set_bounding_box(void)
246 {
247         if (get_color() == I2C_COLOR_RED) {
248                 strat_infos.area_bbox.x1 = 300;
249                 strat_infos.area_bbox.y1 = 200;
250                 strat_infos.area_bbox.x2 = 2720; /* needed for c1 */
251                 strat_infos.area_bbox.y2 = 1800;
252         }
253         else {
254                 strat_infos.area_bbox.x1 = 200;
255                 strat_infos.area_bbox.y1 = 300;
256                 strat_infos.area_bbox.x2 = 2720; /* needed for c1 */
257                 strat_infos.area_bbox.y2 = 1900;
258         }
259
260         polygon_set_boundingbox(strat_infos.area_bbox.x1,
261                                 strat_infos.area_bbox.y1,
262                                 strat_infos.area_bbox.x2,
263                                 strat_infos.area_bbox.y2);
264 }
265
266 /* called before each strat, and before the start switch */
267 void strat_preinit(void)
268 {
269         time_reset();
270         interrupt_traj_reset();
271         mainboard.flags =  DO_ENCODERS | DO_CS | DO_RS |
272                 DO_POS | DO_BD | DO_POWER;
273
274 #ifndef HOST_VERSION
275         i2c_mechboard_mode_init();
276         if (get_color() == I2C_COLOR_RED)
277                 i2c_mechboard_mode_prepare_pickup(I2C_LEFT_SIDE);
278         else
279                 i2c_mechboard_mode_prepare_pickup(I2C_RIGHT_SIDE);
280 #endif
281         strat_dump_conf();
282         strat_dump_infos(__FUNCTION__);
283 }
284
285 void strat_dump_conf(void)
286 {
287         if (!strat_infos.dump_enabled)
288                 return;
289
290         printf_P(PSTR("-- conf --\r\n"));
291
292         printf_P(PSTR("  one build on disc: "));
293         if (strat_infos.conf.flags & STRAT_CONF_ONLY_ONE_ON_DISC)
294                 printf_P(PSTR("on\r\n"));
295         else
296                 printf_P(PSTR("off\r\n"));
297
298         printf_P(PSTR("  bypass static2: "));
299         if (strat_infos.conf.flags & STRAT_CONF_BYPASS_STATIC2)
300                 printf_P(PSTR("on\r\n"));
301         else
302                 printf_P(PSTR("off\r\n"));
303
304         printf_P(PSTR("  take one lintel: "));
305         if (strat_infos.conf.flags & STRAT_CONF_TAKE_ONE_LINTEL)
306                 printf_P(PSTR("on\r\n"));
307         else
308                 printf_P(PSTR("off\r\n"));
309
310         printf_P(PSTR("  skip this temple when temple check fails: "));
311         if (strat_infos.conf.flags & STRAT_CONF_SKIP_WHEN_CHECK_FAILS)
312                 printf_P(PSTR("on\r\n"));
313         else
314                 printf_P(PSTR("off\r\n"));
315
316         printf_P(PSTR("  store static2: "));
317         if (strat_infos.conf.flags & STRAT_CONF_STORE_STATIC2)
318                 printf_P(PSTR("on\r\n"));
319         else
320                 printf_P(PSTR("off\r\n"));
321
322         printf_P(PSTR("  (big3) try to build a temple with 3 lintels: "));
323         if (strat_infos.conf.flags & STRAT_CONF_BIG_3_TEMPLE)
324                 printf_P(PSTR("on\r\n"));
325         else
326                 printf_P(PSTR("off\r\n"));
327
328         printf_P(PSTR("  early opponent scan: "));
329         if (strat_infos.conf.flags & STRAT_CONF_EARLY_SCAN)
330                 printf_P(PSTR("on\r\n"));
331         else
332                 printf_P(PSTR("off\r\n"));
333
334         printf_P(PSTR("  push opponent columns: "));
335         if (strat_infos.conf.flags & STRAT_CONF_PUSH_OPP_COLS)
336                 printf_P(PSTR("on\r\n"));
337         else
338                 printf_P(PSTR("off\r\n"));
339
340         printf_P(PSTR("  scan opponent min time: %d\r\n"),
341                  strat_infos.conf.scan_opp_min_time);
342         printf_P(PSTR("  delay between oppnent scan: %d\r\n"),
343                  strat_infos.conf.delay_between_opp_scan);
344         printf_P(PSTR("  scan our min time: %d\r\n"),
345                  strat_infos.conf.scan_our_min_time);
346         printf_P(PSTR("  delay between our scan: %d\r\n"),
347                  strat_infos.conf.delay_between_our_scan);
348         printf_P(PSTR("  wait opponent gone before scan: %d\r\n"),
349                  strat_infos.conf.wait_opponent);
350         printf_P(PSTR("  lintel min time: %d\r\n"),
351                  strat_infos.conf.lintel_min_time);
352         printf_P(PSTR("  scan_opp_angle: %d\r\n"),
353                  strat_infos.conf.scan_opp_angle);
354 }
355
356 void strat_dump_temple(struct temple *temple)
357 {
358         if (!strat_infos.dump_enabled)
359                 return;
360
361         printf_P(PSTR("  temple %p (%s): "), temple, temple->zone->name);
362
363         if (temple->flags & TEMPLE_F_MONOCOL)
364                 printf_P(PSTR("MONOCOL "));
365         else
366                 printf_P(PSTR("BICOL "));
367
368         if (temple->flags & TEMPLE_F_ON_DISC)
369                 printf_P(PSTR("ON_DISC "));
370         else
371                 printf_P(PSTR("ON_ZONE_0_1 "));
372         
373         if (temple->flags & TEMPLE_F_OPPONENT)
374                 printf_P(PSTR("OPPONENT "));
375         else
376                 printf_P(PSTR("OURS "));
377
378         if (temple->flags & TEMPLE_F_LINTEL)
379                 printf_P(PSTR("LIN_ON_TOP "));
380         else
381                 printf_P(PSTR("COL_ON_TOP "));
382
383         printf_P(PSTR("\r\n"));
384
385         printf_P(PSTR("   pos=(%d,%d,%d) ckpt=(%d,%d) ltime=%d\r\n"),
386                  temple->x, temple->y, temple->a,
387                  temple->checkpoint_x, temple->checkpoint_y,
388                  temple->last_try_time);
389         printf_P(PSTR("   L: lev=%d da=%d,%d\r\n"),
390                  temple->level_l, temple->dist_l, temple->angle_l);
391         printf_P(PSTR("   R: lev=%d da=%d,%d\r\n"),
392                  temple->level_l, temple->dist_l, temple->angle_l);
393 }
394
395 void strat_dump_zone(struct build_zone *zone)
396 {
397         if (!strat_infos.dump_enabled)
398                 return;
399
400         printf_P(PSTR("  zone %s: "), zone->name);
401
402         if (zone->flags & ZONE_F_DISC)
403                 printf_P(PSTR("DISC "));
404         else if (zone->flags & ZONE_F_ZONE1)
405                 printf_P(PSTR("ZONE1 "));
406         else if (zone->flags & ZONE_F_ZONE0)
407                 printf_P(PSTR("ZONE0 "));
408
409         if (zone->flags & ZONE_F_BUSY)
410                 printf_P(PSTR("BUSY "));
411         else
412                 printf_P(PSTR("FREE "));
413         
414         printf_P(PSTR("\r\n"));
415
416         printf_P(PSTR("    lev=%d ckpt=(%d,%d) ltime=%d\r\n"),
417                  zone->level,
418                  zone->checkpoint_x, zone->checkpoint_y,
419                  zone->last_try_time);
420 }
421
422 void strat_dump_static_cols(void)
423 {
424         if (!strat_infos.dump_enabled)
425                 return;
426
427         printf_P(PSTR("  static cols: l0=%d l1=%d l2=%d\r\n"),
428                  strat_infos.s_cols.flags & STATIC_COL_LINE0_DONE,
429                  strat_infos.s_cols.flags & STATIC_COL_LINE1_DONE,
430                  strat_infos.s_cols.flags & STATIC_COL_LINE2_DONE);
431 }
432
433 void strat_dump_col_disp(void)
434 {
435         if (!strat_infos.dump_enabled)
436                 return;
437
438         printf_P(PSTR("  c1 cnt=%d ltt=%d\r\n"),
439                  strat_infos.c1.count, strat_infos.c1.last_try_time);
440         printf_P(PSTR("  c2 cnt=%d ltt=%d\r\n"),
441                  strat_infos.c2.count, strat_infos.c2.last_try_time);
442         printf_P(PSTR("  c3 cnt=%d ltt=%d\r\n"),
443                  strat_infos.c3.count, strat_infos.c3.last_try_time);
444 }
445
446 void strat_dump_lin_disp(void)
447 {
448         if (!strat_infos.dump_enabled)
449                 return;
450         printf_P(PSTR("  l1 cnt=%d ltt=%d\r\n"),
451                  strat_infos.l1.count, strat_infos.l1.last_try_time);
452         printf_P(PSTR("  l2 cnt=%d ltt=%d\r\n"),
453                  strat_infos.l2.count, strat_infos.l2.last_try_time);
454
455 }
456
457 void strat_dump_all_temples(void)
458 {
459         struct temple *temple;
460         uint8_t i;
461
462         if (!strat_infos.dump_enabled)
463                 return;
464
465         for (i=0; i<MAX_TEMPLE; i++) {
466                 temple = &strat_infos.temple_list[i];
467                 if (!(temple->flags & TEMPLE_F_VALID))
468                         continue;
469                 strat_dump_temple(temple);
470         }
471 }
472
473 void strat_dump_all_zones(void)
474 {
475         struct build_zone *zone;
476         uint8_t i;
477
478         if (!strat_infos.dump_enabled)
479                 return;
480
481         for (i=0; i<MAX_ZONE; i++) {
482                 zone = &strat_infos.zone_list[i];
483                 if (!(zone->flags & ZONE_F_VALID))
484                         continue;
485                 strat_dump_zone(zone);
486         }
487 }
488
489 /* display current information about the state of the game */
490 void strat_dump_infos(const char *caller)
491 {
492         if (!strat_infos.dump_enabled)
493                 return;
494
495         printf_P(PSTR("%s() dump strat infos:\r\n"), caller);
496         strat_dump_static_cols();
497         strat_dump_col_disp();
498         strat_dump_lin_disp();
499         strat_dump_all_temples();
500         strat_dump_all_zones();
501 }
502
503 /* init current area state before a match. Dump update user conf
504  * here */
505 void strat_reset_infos(void)
506 {
507         uint8_t i;
508
509         /* /!\ don't do a big memset() as there is static data */
510         strat_infos.s_cols.flags = 0;
511         strat_infos.c1.count = 5;
512         strat_infos.c1.last_try_time = 0;
513         strat_infos.c2.count = 5;
514         strat_infos.c2.last_try_time = 0;
515         strat_infos.c3.count = 5;
516         strat_infos.c3.last_try_time = 0;
517         strat_infos.l1.count = 1;
518         strat_infos.l1.last_try_time = 0;
519         strat_infos.l2.count = 1;
520         strat_infos.l2.last_try_time = 0;
521
522         strat_infos.taken_lintel = 0;
523         strat_infos.col_in_boobs = 0;
524         strat_infos.lazy_pickup_done = 0;
525         strat_infos.i2c_loaded_skipped = 0;
526         
527         memset(strat_infos.temple_list, 0, sizeof(strat_infos.temple_list));
528
529         for (i=0; i<MAX_ZONE; i++)
530                 strat_infos.zone_list[i].flags = ZONE_F_VALID;
531         strat_infos.zone_list[ZONE_DISC_NUM].flags |= ZONE_F_DISC;
532         strat_infos.zone_list[ZONE_1A_NUM].flags |= ZONE_F_ZONE1;
533         strat_infos.zone_list[ZONE_1B_NUM].flags |= ZONE_F_ZONE1;
534         strat_infos.zone_list[ZONE_0A_NUM].flags |= ZONE_F_ZONE0;
535         strat_infos.zone_list[ZONE_0B_NUM].flags |= ZONE_F_ZONE0;
536
537         strat_set_bounding_box();
538
539         /* set lintel position, depending on color */
540         if (mainboard.our_color == I2C_COLOR_RED) {
541                 strat_infos.l1.x = 912;
542                 strat_infos.l2.x = 1312;
543         }
544         else {
545                 strat_infos.l1.x = 888;
546                 strat_infos.l2.x = 1288;
547         }
548 }
549
550 /* call it just before launching the strat */
551 void strat_init(void)
552 {
553 #ifdef HOST_VERSION
554         printf("not implemented\n");
555 #else
556         pickup_wheels_on();
557         strat_reset_infos();
558
559         /* we consider that the color is correctly set */
560
561         strat_set_speed(SPEED_DIST_FAST, SPEED_ANGLE_FAST);
562         time_reset();
563         interrupt_traj_reset();
564
565         /* used in strat_base for END_TIMER */
566         mainboard.flags = DO_ENCODERS | DO_CS | DO_RS | 
567                 DO_POS | DO_BD | DO_TIMER | DO_POWER;
568
569 #ifdef TEST_BEACON
570         beacon_prev_time = 0;
571         beacon_cur_idx = 0;
572 #endif
573 #endif
574 }
575
576
577 /* call it after each strat */
578 void strat_exit(void)
579 {
580 #ifdef HOST_VERSION
581         printf("not implemented\n");
582 #else
583         uint8_t flags;
584
585         pickup_wheels_off();
586         mainboard.flags &= ~(DO_TIMER);
587         strat_hardstop();
588         time_reset();
589         wait_ms(1000);
590         IRQ_LOCK(flags);
591         mainboard.flags &= ~(DO_CS);
592         pwm_ng_set(LEFT_PWM, 0);
593         pwm_ng_set(RIGHT_PWM, 0);
594         IRQ_UNLOCK(flags);
595 #endif
596 }
597
598 /* called periodically */
599 void strat_event(void *dummy)
600 {
601         /* limit speed when opponent is close */
602         strat_limit_speed();
603
604 #ifdef TEST_BEACON
605         beacon_update_samples();
606 #endif
607 }
608
609 #ifndef HOST_VERSION
610 /* do static cols + first temples */
611 static uint8_t strat_beginning(void)
612 {
613         uint8_t err;
614
615         /* don't limit the speed when opponent is near: it can change
616          * the radius of the curves */
617         strat_limit_speed_disable();
618
619         err = strat_static_columns(0);
620
621         strat_limit_speed_enable();
622
623         if (!TRAJ_SUCCESS(err))
624                 return err;
625
626         /* go to disc to build the first temple */
627
628         /* XXX if opponent is near disc, go to zone1 */
629         err = strat_goto_disc(2);
630         if (!TRAJ_SUCCESS(err))
631                 return err;
632         DEBUG(E_USER_STRAT, "disc reached");
633
634         /* can return END_ERROR or END_TIMER, should not happen
635          * here */
636         err = strat_build_new_temple(&strat_infos.zone_list[0]);
637         if (!TRAJ_SUCCESS(err))
638                 return err;
639
640         /* bypass static2 if specified */
641         if (strat_infos.conf.flags & STRAT_CONF_BYPASS_STATIC2) {
642                 err = strat_escape(&strat_infos.zone_list[0], TRAJ_FLAGS_STD);
643                 return err;
644         }
645
646         /* get the last 2 columns, and build them on previous temple */
647         err = strat_static_columns_pass2();
648         if (!TRAJ_SUCCESS(err))
649                 return err;
650
651         /* early opponent scan, for offensive strategy */
652         if (strat_infos.conf.flags & STRAT_CONF_EARLY_SCAN) {
653                 err = strat_pickup_lintels();
654                 /* ignore code */
655
656                 /* try to build on opponent (scan must be enabled) */
657                 err = strat_build_on_opponent_temple();
658                 /* ignore code */
659         }
660
661         return err;
662 }
663 #endif
664
665 /* return true if we need to grab some more elements (lintel/cols) */
666 uint8_t need_more_elements(void)
667 {
668 #ifdef HOST_VERSION
669         printf("not implemented\n");
670 #else
671         if (time_get_s() <= 75) {
672                 /* we have at least one col on each arm, build now */
673                 if ((get_column_count_left() >= 1) && 
674                     (get_column_count_right() >= 1))
675                         return 0;
676         }
677         else {
678                 if (get_column_count())
679                         return 0;
680         }
681 #endif
682         return 1;
683 }
684
685 /* dump state (every 5 s max) */
686 #define DUMP_RATE_LIMIT(dump, last_print)               \
687         do {                                            \
688                 if (time_get_s() - last_print > 5) {    \
689                         dump();                         \
690                         last_print = time_get_s();      \
691                 }                                       \
692         } while (0)
693
694
695 uint8_t strat_main(void)
696 {
697 #ifdef HOST_VERSION
698         printf("not implemented\n");
699         return END_ERROR;
700 #else
701         uint8_t err;
702         struct temple *temple = NULL;
703         struct build_zone *zone = NULL;
704
705         uint8_t last_print_cols = 0;
706         uint8_t last_print_lin = 0;
707         uint8_t last_print_temple = 0;
708         uint8_t last_print_zone = 0;
709
710         /* do static cols + first temple */
711         err = strat_beginning();
712
713         /* skip error code */
714
715         while (1) {
716                 
717                 if (err == END_TIMER) {
718                         DEBUG(E_USER_STRAT, "End of time");
719                         strat_exit();
720                         break;
721                 }
722
723                 /* we have at least one col on each arm, build now */
724                 if (need_more_elements() == 0) {
725                         
726                         /* try to build on opponent, will return
727                          * END_TRAJ without doing anything if
728                          * disabled */
729                         err = strat_build_on_opponent_temple();
730                         if (!TRAJ_SUCCESS(err))
731                                 continue;
732                         if (need_more_elements())
733                                 continue;
734
735                         /* try to scan and build on our temple, will
736                          * return END_TRAJ without doing anything if
737                          * disabled */
738                         err = strat_check_temple_and_build();
739                         if (!TRAJ_SUCCESS(err))
740                                 continue;
741                         if (need_more_elements())
742                                 continue;
743
744                         /* Else, do a simple build, as before */
745
746                         temple = strat_get_best_temple();
747
748                         /* one valid temple found */
749                         if (temple) {
750                                 DUMP_RATE_LIMIT(strat_dump_all_temples, last_print_temple);
751
752                                 err = strat_goto_temple(temple);
753                                 if (!TRAJ_SUCCESS(err))
754                                         continue;
755
756                                 /* can return END_ERROR or END_TIMER,
757                                  * should not happen here */
758                                 err = strat_grow_temple(temple);
759                                 if (!TRAJ_SUCCESS(err))
760                                         continue;
761                                 
762                                 err = strat_escape(temple->zone, TRAJ_FLAGS_STD);
763                                 if (!TRAJ_SUCCESS(err))
764                                         continue;
765
766                                 continue;
767                         }
768
769                         zone = strat_get_best_zone();
770                         if (zone) {
771                                 DUMP_RATE_LIMIT(strat_dump_all_zones, last_print_zone);
772
773                                 DEBUG(E_USER_STRAT, "goto zone %s", zone->name);
774                                 err = strat_goto_build_zone(zone, zone->level);
775                                 if (!TRAJ_SUCCESS(err))
776                                         continue;
777                                 DEBUG(E_USER_STRAT, "zone reached");
778                                 
779                                 /* no error code except END_ERROR, should not happen */
780                                 err = strat_build_new_temple(zone);
781
782                                 err = strat_escape(zone, TRAJ_FLAGS_STD);
783                                 if (!TRAJ_SUCCESS(err))
784                                         continue;
785
786                                 continue;
787                         }
788
789                         /* XXX hey what can we do here... :'( */
790                         DEBUG(E_USER_STRAT, "panic :)");
791                         time_wait_ms(1000);
792                         continue;
793                 }
794
795                 /* else we need some elements (lintels, then columns) */
796                 else {
797                         if (strat_infos.l1.count != 0 && strat_infos.l2.count != 0)
798                                 DUMP_RATE_LIMIT(strat_dump_lin_disp, last_print_lin);
799
800                         err = strat_pickup_lintels();
801                          /* can return an error code, but we have
802                           * nothing to do because pickup_column()
803                           * starts with a goto_and_avoid() */
804                         if (!TRAJ_SUCCESS(err))
805                                 nop();
806                         
807                         DUMP_RATE_LIMIT(strat_dump_col_disp, last_print_cols);
808
809                         err = strat_pickup_columns();
810                         if (!TRAJ_SUCCESS(err))
811                                 nop(); /* nothing to do */
812
813                         /* XXX check here that we have elements, or do
814                          * something else */
815                         /* if we cannot take elements, try to build */
816                 }
817         }
818         return END_TRAJ;
819 #endif
820 }