15e97a0bf66c773023a19800d8d755886f2ccef3
[universe.git] / main.cc
1
2 // Written and (C) by Francois Fleuret
3 // Contact <francois.fleuret@idiap.ch> for comments & bug reports
4
5 #include <iostream>
6 #include <fstream>
7 #include <cmath>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <errno.h>
12 #include <string.h>
13
14 using namespace std;
15
16 #include "misc.h"
17 #include "task.h"
18 #include "simple_window.h"
19 #include "universe.h"
20 #include "retina.h"
21 #include "manipulator.h"
22 #include "intelligence.h"
23 #include "canvas_cairo.h"
24
25 #include "xfig_tracer.h"
26
27 void generate_png(Universe *universe, scalar_t scale, FILE *file) {
28   CanvasCairo canvas(scale, universe->width(), universe->height());
29   universe->draw(&canvas);
30   canvas.write_png(file);
31 }
32
33 // To train
34 // ./main --task hit_shape.task 0 --action-mode=random --nb-ticks=5000 --proportion-for-training=0.5 --save-file=dump.mem --no-window
35
36 // To test
37 // ./main --task hit_shape.task 0 --action-mode=intelligent --load-file=dump.mem
38
39 //////////////////////////////////////////////////////////////////////
40
41 void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
42   if(n_opt + n >= argc) {
43     cerr << "Missing argument for " << argv[n_opt] << "." << endl;
44     cerr << "Expecting " << help << "." << endl;
45     exit(1);
46   }
47 }
48
49 void print_help_and_exit(int e) {
50   cout << "Arguments:" << endl;
51   cout << "  --no-window" << endl;
52   cout << "  --nb-ticks=<int: number of ticks>" << endl;
53   cout << "  --nb-training-iterations=<int: number of training iterations>" << endl;
54   cout << "  --load-file=<filename: dump file>" << endl;
55   cout << "  --save-file=<filename: dump file>" << endl;
56   cout << "  --proportion-for-training=<float: proportion of samples for training>" << endl;
57   cout << "  --action-mode=<idle|random|intelligent>" << endl;
58   cout << "  --task <filename: task to load> <int: degree>" << endl;
59   exit(e);
60 }
61
62 //////////////////////////////////////////////////////////////////////
63
64 int main(int argc, char **argv) {
65
66   const int buffer_size = 256;
67   char intelligence_load_file[buffer_size] = "", intelligence_save_file[buffer_size] = "";
68
69   Task *task = 0;
70   int task_degree = 0;
71
72   int nb_ticks = 10000;
73   int nb_training_iterations = 10;
74   scalar_t proportion_for_training = -1;
75
76   Polygon *grabbed_polygon = 0;
77   scalar_t relative_grab_x = 0, relative_grab_y = 0;
78
79   bool quit = false;
80   bool press_shift = false;
81   enum { IDLE, RANDOM, INTELLIGENT } action_mode = IDLE;
82   bool got_event = true;
83   int current_action = 0;
84
85   scalar_t last_hand_x = 0, last_hand_y = 0;
86   Polygon *last_grabbing = 0;
87   bool no_window = false;
88
89   //////////////////////////////////////////////////////////////////////
90   //                    Parsing the shell arguments
91   //////////////////////////////////////////////////////////////////////
92
93   int i = 1;
94   while(i < argc) {
95     if(argc == 1 || strcmp(argv[i], "--help") == 0) print_help_and_exit(0);
96
97     else if(strcmp(argv[i], "--test") == 0) {
98       test_approximer();
99       exit(0);
100     }
101
102     else if(strcmp(argv[i], "--task") == 0) {
103       check_opt(argc, argv, i, 2, "<filename: task to load> <int: degree>");
104       if(task) {
105         cerr << "Can not load two tasks." << endl;
106         exit(1);
107       }
108       task = load_task(argv[i+1]);
109       task_degree = atoi(argv[i+2]);
110       i += 3;
111
112     } else if(strncmp(argv[i], "--", 2) == 0) {
113       char variable_name[buffer_size] = "", variable_value[buffer_size] = "";
114       char *o = argv[i]+2, *s = variable_name, *u = variable_value;
115       while(*o && *o != '=') *s++ = *o++;
116       if(*o) {
117         o++;
118         while(*o) *u++ = *o++;
119       }
120
121       if(strcmp(variable_name, "nb-ticks") == 0)
122         nb_ticks = atoi(variable_value);
123       else if(strcmp(variable_name, "nb-training-iterations") == 0)
124         nb_training_iterations = atoi(variable_value);
125       else if(strcmp(variable_name, "proportion-for-training") == 0)
126         proportion_for_training = atof(variable_value);
127       else if(strcmp(variable_name, "no-window") == 0)
128         no_window = true;
129       else if(strcmp(variable_name, "save-file") == 0)
130         strcpy(intelligence_save_file, variable_value);
131       else if(strcmp(variable_name, "load-file") == 0)
132         strcpy(intelligence_load_file, variable_value);
133       else if(strcmp(variable_name, "action-mode") == 0) {
134         if(strcmp(variable_value, "idle") == 0) action_mode = IDLE;
135         else if(strcmp(variable_value, "random") == 0) action_mode = RANDOM;
136         else if(strcmp(variable_value, "intelligent") == 0) action_mode = INTELLIGENT;
137         else {
138           cerr << "The only known modes are idle, random and intelligent" << endl;
139           exit(1);
140         }
141       } else {
142         cerr << "Unknown option " << argv[i] << endl;
143         print_help_and_exit(1);
144       }
145       i++;
146     } else {
147       cerr << "Unknown option " << argv[i] << endl;
148       print_help_and_exit(1);
149     }
150   }
151
152   cout << "FlatLand, a toy universe for goal-planning experiments." << endl;
153
154   if(!task) {
155     task = load_task("dummy.so");
156     task_degree = 0;
157   }
158
159   cout << "Loaded task " << task->name()
160        << " with degree " << task_degree << "/" << task->nb_degrees()
161        << endl;
162
163   if(task_degree < 0 || task_degree >= task->nb_degrees()) {
164     cout << "Invalid degree: " << task_degree << "." << endl;
165     exit(1);
166   }
167
168   //////////////////////////////////////////////////////////////////////
169   //                      Various initializations
170   //////////////////////////////////////////////////////////////////////
171
172   Universe universe(100, task->width(), task->height());
173   task->init(&universe, task_degree);
174   Retina retina(&universe);
175   Manipulator manipulator(task);
176   manipulator.force_move(task->width()/2, task->height()/2);
177   retina.set_location(manipulator.hand_x(), manipulator.hand_y());
178
179   SimpleWindow *window_main = 0, *window_brain = 0;
180   int window_main_fd = -1;
181
182 #ifdef CAIRO_SUPPORT
183   // cairo_t *window_main_cairo_cr = 0;
184 #endif
185
186   MapConcatener sensory_map(2);
187   sensory_map.add_map(&retina);
188   sensory_map.add_map(&manipulator);
189   sensory_map.init();
190
191   MapExpander expanded_map(1000);
192   expanded_map.set_input(&sensory_map);
193   expanded_map.init();
194
195   Intelligence intelligence(&expanded_map, &manipulator, nb_ticks + 1, nb_training_iterations);
196   intelligence.update(0, 0.0);
197
198   if(intelligence_load_file[0]) {
199     cout << "Loading from " << intelligence_load_file << " ... " ;
200     cout.flush();
201     ifstream in(intelligence_load_file);
202     if(in.fail()) {
203       cerr << "error reading " << intelligence_load_file << "." << endl;
204       exit(1);
205     }
206     intelligence.load(in);
207     cout << "done." << endl ;
208   }
209
210   if(no_window) {
211     cout << "Started without windows." << endl;
212   } else {
213     window_main = new SimpleWindow("Universe (main window)", 4, 4, task->width(), task->height());
214     window_main_fd = window_main->file_descriptor();
215     window_main->map();
216 #ifdef CAIRO_SUPPORT
217     // window_main_cairo_cr = window_main->get_cairo_context_resource();
218 #endif
219     cout << "When the main window has the focus, press `q' to quit and click and drag to move" << endl
220          << "objects." << endl;
221     window_brain = new SimpleWindow("Universe (brain)",
222                                     12 + task->width(), 4,
223                                     retina.width(), retina.height() + manipulator.parameter_height());
224     window_brain->map();
225   }
226
227   int tick = 0;
228   time_t last_t = 0;
229   scalar_t sum_reward = 0;
230
231   //////////////////////////////////////////////////////////////////////
232   //                         The main loop
233   //////////////////////////////////////////////////////////////////////
234
235   while(!quit && tick != nb_ticks) {
236
237     int r;
238     fd_set fds;
239
240
241     if(window_main) {
242       struct timeval tv;
243       FD_ZERO (&fds);
244       FD_SET (window_main_fd, &fds);
245       tv.tv_sec = 0;
246       tv.tv_usec = 5000; // 0.05s
247       r = select(window_main_fd + 1, &fds, 0, 0, &tv);
248     } else r = 0;
249
250     time_t t = time(0);
251
252     if(t > last_t) {
253       last_t = t;
254       cout << tick << " " << sum_reward << "              \r"; cout.flush();
255     }
256
257     if(r == 0) { // No window event, thus it's the clock tick
258
259         int nb_it = 10;
260
261         bool changed = got_event;
262         got_event = false;
263
264         switch(action_mode) {
265         case IDLE:
266           break;
267         case RANDOM:
268           current_action = manipulator.random_action();
269           break;
270         case INTELLIGENT:
271           current_action = intelligence.best_action();
272           //           if(drand48() < 0.5) current_action = intelligence.best_action();
273           //           else                current_action = manipulator.random_action();
274           break;
275         }
276
277         manipulator.do_action(current_action);
278
279         scalar_t dt = 1.0/scalar_t(nb_it);
280         for(int k = 0; k < nb_it; k++) {
281           manipulator.update(dt, &universe);
282           task->update(dt, &universe, &manipulator);
283           changed |= universe.update(dt);
284         }
285
286         tick++;
287
288         changed |= manipulator.hand_x() != last_hand_x ||
289           manipulator.hand_y() != last_hand_y ||
290           manipulator.grabbing() != last_grabbing;
291
292         scalar_t reward = task->reward(&universe, &manipulator);
293         sum_reward += abs(reward);
294         intelligence.update(current_action, reward);
295         expanded_map.update_map();
296
297         if(changed) {
298           last_hand_x = manipulator.hand_x();
299           last_hand_y = manipulator.hand_y();
300           last_grabbing = manipulator.grabbing();
301
302           retina.set_location(manipulator.hand_x(),
303                               manipulator.hand_y());
304
305           if(window_main) {
306             window_main->color(0.0, 0.0, 0.0);
307             window_main->color(1.0, 1.0, 1.0);
308             window_main->fill();
309
310 // #ifdef CAIRO_SUPPORT
311             // CanvasCairo cc;
312             // cc._context_resource = window_main_cairo_cr;
313             // universe.draw(&cc);
314 // #else
315             universe.draw(window_main);
316 // #endif
317
318             task->draw(window_main);
319             manipulator.draw_on_universe(window_main);
320             retina.draw_on_universe(window_main);
321
322             if(grabbed_polygon) {
323               int x, y, delta = 3;
324               x = int(grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y));
325               y = int(grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y));
326               window_main->color(0.0, 0.0, 0.0);
327               window_main->draw_line(x - delta, y, x + delta, y);
328               window_main->draw_line(x, y - delta, x, y + delta);
329             }
330
331             window_main->show();
332
333             if(window_brain) {
334               retina.draw_parameters(0, 0, window_brain);
335               manipulator.draw_parameters(0, retina.height() + 1, window_brain);
336               window_brain->show();
337             }
338           }
339         }
340
341     } else if(r > 0) { // We got window events, let's process them
342
343       got_event = true;
344
345       if(FD_ISSET(window_main_fd, &fds)) {
346
347         SimpleEvent se;
348
349         do {
350           se = window_main->event();
351
352           switch(se.type) {
353
354           case SimpleEvent::MOUSE_CLICK_PRESS:
355             {
356               switch(se.button) {
357
358               case 1:
359                 if(press_shift) {
360                   manipulator.force_move(se.x, se.y);
361                   manipulator.do_action(Manipulator::ACTION_GRAB);
362                 } else {
363                   grabbed_polygon = universe.pick_polygon(se.x, se.y);
364                   if(grabbed_polygon) {
365                     relative_grab_x = grabbed_polygon->relative_x(se.x, se.y);
366                     relative_grab_y = grabbed_polygon->relative_y(se.x, se.y);
367                   }
368                 }
369                 break;
370               case 4:
371                 {
372                   Polygon *g = universe.pick_polygon(se.x, se.y);
373                   if(g) g->_theta += M_PI/32;
374                 }
375                 break;
376               case 5:
377                 {
378                   Polygon *g = universe.pick_polygon(se.x, se.y);
379                   if(g) g->_theta -= M_PI/32;
380                 }
381                 break;
382               }
383             }
384             break;
385
386           case SimpleEvent::MOUSE_CLICK_RELEASE:
387             switch(se.button) {
388             case 1:
389               if(press_shift) manipulator.do_action(Manipulator::ACTION_RELEASE);
390               else            grabbed_polygon = 0;
391               break;
392             default:
393               break;
394             }
395
396           case SimpleEvent::MOUSE_MOTION:
397             {
398               if(press_shift) {
399                 manipulator.force_move(se.x, se.y);
400               } else if(grabbed_polygon) {
401                 scalar_t xf, yf, force_x, force_y, f, fmax = 100;
402                 xf = grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y);
403                 yf = grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y);
404                 force_x = se.x - xf;
405                 force_y = se.y - yf;
406                 f = sqrt(sq(force_x) + sq(force_y));
407                 if(f > fmax) { force_x = (force_x * fmax)/f; force_y = (force_y * fmax)/f; }
408                 grabbed_polygon->apply_force(0.1, xf, yf, force_x, force_y);
409               }
410               break;
411             }
412             break;
413
414           case SimpleEvent::KEY_PRESS:
415             {
416               if(strcmp(se.key, "q") == 0) {
417                 quit = true;
418               }
419
420               else if(strcmp(se.key, "s") == 0) {
421
422                 retina.save_as_ppm("/tmp/retina.ppm");
423                 cout << "Retina screen shot saved in /tmp/retina.ppm" << endl;
424
425                 {
426                   XFigTracer tracer("/tmp/universe.fig");
427                   universe.print_xfig(&tracer);
428                 }
429
430 #ifdef CAIRO_SUPPORT
431                 {
432                   FILE *file = fopen("/tmp/universe.png", "w");
433                   generate_png(&universe, 0.25, file);
434                   // generate_png(task->width(), task->height(), &universe, "/tmp/universe.png");
435                   cout << "Universe image saved in /tmp/universe.png" << endl;
436                 }
437 #endif
438
439               }
440
441               else if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) {
442                 press_shift = true;
443               }
444
445               else if(strcmp(se.key, "Up") == 0) {
446                 manipulator.do_action(Manipulator::ACTION_MOVE_UP);
447               }
448
449               else if(strcmp(se.key, "Right") == 0) {
450                 manipulator.do_action(Manipulator::ACTION_MOVE_RIGHT);
451               }
452
453               else if(strcmp(se.key, "Down") == 0) {
454                 manipulator.do_action(Manipulator::ACTION_MOVE_DOWN);
455               }
456
457               else if(strcmp(se.key, "Left") == 0) {
458                 manipulator.do_action(Manipulator::ACTION_MOVE_LEFT);
459               }
460
461               else if(strcmp(se.key, "g") == 0) {
462                 manipulator.do_action(Manipulator::ACTION_GRAB);
463               }
464
465               else if(strcmp(se.key, "r") == 0) {
466                 manipulator.do_action(Manipulator::ACTION_RELEASE);
467               }
468
469               else if(strcmp(se.key, "space") == 0) {
470                 switch(action_mode) {
471                 case IDLE:
472                   action_mode = RANDOM;
473                   cout << "Switched to random mode" << endl;
474                   break;
475                 case RANDOM:
476                   action_mode = INTELLIGENT;
477                   cout << "Switched to intelligent mode" << endl;
478                   break;
479                 case INTELLIGENT:
480                   cout << "Switched to idle mode" << endl;
481                   action_mode = IDLE;
482                   break;
483                 }
484               }
485
486               else cout << "Undefined key " << se.key << endl;
487             }
488             break;
489
490           case SimpleEvent::KEY_RELEASE:
491             {
492               if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) press_shift = false;
493             }
494             break;
495
496           default:
497             break;
498
499           }
500         } while(se.type != SimpleEvent::NO_EVENT);
501       } else {
502         cerr << "Error on select: " << strerror(errno) << endl;
503         exit(1);
504       }
505     }
506   }
507
508   if(proportion_for_training > 0) {
509     cout << "Learning ... "; cout.flush();
510     intelligence.learn(proportion_for_training);
511     cout << "done." << endl;
512   }
513
514   if(intelligence_save_file[0]) {
515     cout << "Saving to " << intelligence_save_file << endl; cout.flush();
516     ofstream os(intelligence_save_file);
517     if(os.fail()) {
518       cerr << "error writing " << intelligence_save_file << "." << endl;
519       exit(1);
520     }
521     cout << "done." << endl;
522     intelligence.save(os);
523   }
524
525   delete window_brain;
526   delete window_main;
527
528 }