2 // Written and (C) by Francois Fleuret
3 // Contact <francois.fleuret@idiap.ch> for comments & bug reports
18 #include "simple_window.h"
21 #include "manipulator.h"
22 #include "intelligence.h"
24 #include "canvas_cairo.h"
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);
33 // ./main --task hit_shape.task 0 --action-mode=random --nb-ticks=5000 --proportion-for-training=0.5 --save-file=dump.mem --no-window
36 // ./main --task hit_shape.task 0 --action-mode=intelligent --load-file=dump.mem
38 //////////////////////////////////////////////////////////////////////
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;
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;
61 //////////////////////////////////////////////////////////////////////
63 int main(int argc, char **argv) {
65 const int buffer_size = 256;
66 char intelligence_load_file[buffer_size] = "", intelligence_save_file[buffer_size] = "";
72 int nb_training_iterations = 10;
73 scalar_t proportion_for_training = -1;
75 Polygon *grabbed_polygon = 0;
76 scalar_t relative_grab_x = 0, relative_grab_y = 0;
79 bool press_shift = false;
80 enum { IDLE, RANDOM, INTELLIGENT } action_mode = IDLE;
81 bool got_event = true;
82 int current_action = 0;
84 scalar_t last_hand_x = 0, last_hand_y = 0;
85 Polygon *last_grabbing = 0;
86 bool no_window = false;
88 //////////////////////////////////////////////////////////////////////
89 // Parsing the shell arguments
90 //////////////////////////////////////////////////////////////////////
94 if(argc == 1 || strcmp(argv[i], "--help") == 0) print_help_and_exit(0);
96 else if(strcmp(argv[i], "--test") == 0) {
101 else if(strcmp(argv[i], "--task") == 0) {
102 check_opt(argc, argv, i, 2, "<filename: task to load> <int: degree>");
104 cerr << "Can not load two tasks." << endl;
107 task = load_task(argv[i+1]);
108 task_degree = atoi(argv[i+2]);
111 } else if(strncmp(argv[i], "--", 2) == 0) {
112 char variable_name[buffer_size] = "", variable_value[buffer_size] = "";
113 char *o = argv[i]+2, *s = variable_name, *u = variable_value;
114 while(*o && *o != '=') *s++ = *o++;
117 while(*o) *u++ = *o++;
120 if(strcmp(variable_name, "nb-ticks") == 0) {
121 nb_ticks = atoi(variable_value);
122 } else if(strcmp(variable_name, "nb-training-iterations") == 0) {
123 nb_training_iterations = atoi(variable_value);
124 } else if(strcmp(variable_name, "proportion-for-training") == 0) {
125 proportion_for_training = atof(variable_value);
126 } else if(strcmp(variable_name, "no-window") == 0) {
128 } else if(strcmp(variable_name, "save-file") == 0) {
129 strcpy(intelligence_save_file, variable_value);
130 } else if(strcmp(variable_name, "load-file") == 0) {
131 strcpy(intelligence_load_file, variable_value);
132 } else if(strcmp(variable_name, "action-mode") == 0) {
133 if(strcmp(variable_value, "idle") == 0) {
135 } else if(strcmp(variable_value, "random") == 0) {
136 action_mode = RANDOM;
137 } else if(strcmp(variable_value, "intelligent") == 0) {
138 action_mode = INTELLIGENT;
140 cerr << "The only known modes are idle, random and intelligent" << endl;
144 cerr << "Unknown option " << argv[i] << endl;
145 print_help_and_exit(1);
149 cerr << "Unknown option " << argv[i] << endl;
150 print_help_and_exit(1);
154 cout << "FlatLand, a toy universe for goal-planning experiments." << endl;
157 task = load_task("dummy.so");
161 cout << "Loaded task " << task->name()
162 << " with degree " << task_degree << "/" << task->nb_degrees()
165 if(task_degree < 0 || task_degree >= task->nb_degrees()) {
166 cout << "Invalid degree: " << task_degree << "." << endl;
170 //////////////////////////////////////////////////////////////////////
171 // Various initializations
172 //////////////////////////////////////////////////////////////////////
174 Universe universe(100, task->width(), task->height());
175 task->init(&universe, task_degree);
176 Manipulator manipulator(task);
177 manipulator.force_move(task->width()/2, task->height()/2);
179 SimpleWindow *window_main = 0;
180 int window_main_fd = -1;
183 // cairo_t *window_main_cairo_cr = 0;
186 MapConcatener sensory_map(2);
187 sensory_map.add_map(&manipulator);
190 MapExpander expanded_map(1000);
191 expanded_map.set_input(&sensory_map);
194 Intelligence intelligence(&expanded_map, &manipulator, nb_ticks + 1, nb_training_iterations);
195 intelligence.update(0, 0.0);
197 if(intelligence_load_file[0]) {
198 cout << "Loading from " << intelligence_load_file << " ... " ;
200 ifstream in(intelligence_load_file);
202 cerr << "error reading " << intelligence_load_file << "." << endl;
205 intelligence.load(in);
206 cout << "done." << endl ;
210 cout << "Started without windows." << endl;
212 window_main = new SimpleWindow("Universe (main window)", 4, 4, task->width(), task->height());
213 window_main_fd = window_main->file_descriptor();
216 // window_main_cairo_cr = window_main->get_cairo_context_resource();
218 cout << "When the main window has the focus, press `q' to quit and click and drag to move" << endl
219 << "objects." << endl;
224 scalar_t sum_reward = 0;
226 //////////////////////////////////////////////////////////////////////
228 //////////////////////////////////////////////////////////////////////
230 while(!quit && tick != nb_ticks) {
238 sprintf(buffer, "frame-%06d.png", tick);
239 FILE *file = fopen(buffer, "w");
240 generate_png(&universe, 0.25, file);
241 cout << "Universe image saved in " << buffer << endl;
249 FD_SET (window_main_fd, &fds);
251 tv.tv_usec = 5000; // 0.05s
252 r = select(window_main_fd + 1, &fds, 0, 0, &tv);
259 cout << tick << " " << sum_reward << " \r"; cout.flush();
262 if(r == 0) { // No window event, thus it's the clock tick
266 bool changed = got_event;
269 switch(action_mode) {
273 current_action = manipulator.random_action();
276 current_action = intelligence.best_action();
277 // if(drand48() < 0.5) current_action = intelligence.best_action();
278 // else current_action = manipulator.random_action();
282 manipulator.do_action(current_action);
284 scalar_t dt = 1.0/scalar_t(nb_it);
285 for(int k = 0; k < nb_it; k++) {
286 manipulator.update(dt, &universe);
287 task->update(dt, &universe, &manipulator);
288 universe.apply_gravity(dt, 0.0, 2.0);
289 changed |= universe.update(dt);
294 changed |= manipulator.hand_x() != last_hand_x ||
295 manipulator.hand_y() != last_hand_y ||
296 manipulator.grabbing() != last_grabbing;
298 scalar_t reward = task->reward(&universe, &manipulator);
299 sum_reward += abs(reward);
300 intelligence.update(current_action, reward);
301 expanded_map.update_map();
304 last_hand_x = manipulator.hand_x();
305 last_hand_y = manipulator.hand_y();
306 last_grabbing = manipulator.grabbing();
309 window_main->color(0.0, 0.0, 0.0);
310 window_main->color(1.0, 1.0, 1.0);
312 universe.draw(window_main);
314 task->draw(window_main);
315 manipulator.draw_on_universe(window_main);
317 if(grabbed_polygon) {
319 x = int(grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y));
320 y = int(grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y));
321 window_main->color(0.0, 0.0, 0.0);
322 window_main->draw_line(x - delta, y, x + delta, y);
323 window_main->draw_line(x, y - delta, x, y + delta);
330 } else if(r > 0) { // We got window events, let's process them
334 if(FD_ISSET(window_main_fd, &fds)) {
339 se = window_main->event();
343 case SimpleEvent::MOUSE_CLICK_PRESS:
349 manipulator.force_move(se.x, se.y);
350 manipulator.do_action(Manipulator::ACTION_GRAB);
352 grabbed_polygon = universe.pick_polygon(se.x, se.y);
353 if(grabbed_polygon) {
354 relative_grab_x = grabbed_polygon->relative_x(se.x, se.y);
355 relative_grab_y = grabbed_polygon->relative_y(se.x, se.y);
361 Polygon *g = universe.pick_polygon(se.x, se.y);
362 if(g) g->_theta += M_PI/32;
367 Polygon *g = universe.pick_polygon(se.x, se.y);
368 if(g) g->_theta -= M_PI/32;
375 case SimpleEvent::MOUSE_CLICK_RELEASE:
378 if(press_shift) manipulator.do_action(Manipulator::ACTION_RELEASE);
379 else grabbed_polygon = 0;
385 case SimpleEvent::MOUSE_MOTION:
388 manipulator.force_move(se.x, se.y);
389 } else if(grabbed_polygon) {
390 scalar_t xf, yf, force_x, force_y, f, fmax = 100;
391 xf = grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y);
392 yf = grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y);
395 f = sqrt(sq(force_x) + sq(force_y));
396 if(f > fmax) { force_x = (force_x * fmax)/f; force_y = (force_y * fmax)/f; }
397 grabbed_polygon->apply_force(0.1, xf, yf, force_x, force_y);
403 case SimpleEvent::KEY_PRESS:
405 if(strcmp(se.key, "q") == 0) {
409 else if(strcmp(se.key, "s") == 0) {
412 Plotter plotter(int(universe.width()), int(universe.height()), 4);
413 plotter.save_as_ppm(&universe, "/tmp/plotter.ppm", 16);
418 FILE *file = fopen("/tmp/screenshot.png", "w");
419 generate_png(&universe, 0.25, file);
420 cout << "Universe image saved in /tmp/screenshot.png" << endl;
427 else if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) {
431 else if(strcmp(se.key, "Up") == 0) {
432 manipulator.do_action(Manipulator::ACTION_MOVE_UP);
435 else if(strcmp(se.key, "Right") == 0) {
436 manipulator.do_action(Manipulator::ACTION_MOVE_RIGHT);
439 else if(strcmp(se.key, "Down") == 0) {
440 manipulator.do_action(Manipulator::ACTION_MOVE_DOWN);
443 else if(strcmp(se.key, "Left") == 0) {
444 manipulator.do_action(Manipulator::ACTION_MOVE_LEFT);
447 else if(strcmp(se.key, "g") == 0) {
448 manipulator.do_action(Manipulator::ACTION_GRAB);
451 else if(strcmp(se.key, "r") == 0) {
452 manipulator.do_action(Manipulator::ACTION_RELEASE);
455 else if(strcmp(se.key, "space") == 0) {
456 switch(action_mode) {
458 action_mode = RANDOM;
459 cout << "Switched to random mode" << endl;
462 action_mode = INTELLIGENT;
463 cout << "Switched to intelligent mode" << endl;
466 cout << "Switched to idle mode" << endl;
472 else cout << "Undefined key " << se.key << endl;
476 case SimpleEvent::KEY_RELEASE:
478 if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) press_shift = false;
486 } while(se.type != SimpleEvent::NO_EVENT);
488 cerr << "Error on select: " << strerror(errno) << endl;
494 if(proportion_for_training > 0) {
495 cout << "Learning ... "; cout.flush();
496 intelligence.learn(proportion_for_training);
497 cout << "done." << endl;
500 if(intelligence_save_file[0]) {
501 cout << "Saving to " << intelligence_save_file << endl; cout.flush();
502 ofstream os(intelligence_save_file);
504 cerr << "error writing " << intelligence_save_file << "." << endl;
507 cout << "done." << endl;
508 intelligence.save(os);