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