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