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