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