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