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"
23 #include "xfig_tracer.h"
28 static cairo_status_t write_cairo_to_file(void *closure,
29 const unsigned char *data,
30 unsigned int length) {
31 fwrite(data, 1, length, (FILE *) closure);
32 return CAIRO_STATUS_SUCCESS;
35 void generate_png(Universe *universe, scalar_t scale, FILE *file) {
37 const int width = int(universe->width() * scale);
38 const int height = int(universe->height() * scale);
40 cairo_surface_t *image;
41 cairo_t* context_resource;
44 data = new unsigned char [width * height * depth];
46 image = cairo_image_surface_create_for_data(data,
52 context_resource = cairo_create(image);
54 cairo_scale(context_resource, scale, scale);
56 cairo_set_source_rgb(context_resource, 1.0, 1.0, 1.0);
58 cairo_rectangle(context_resource, 0, 0,
59 universe->width(), universe->height());
61 cairo_fill(context_resource);
63 universe->draw(context_resource);
65 cairo_surface_write_to_png_stream(image, write_cairo_to_file, file);
67 cairo_destroy(context_resource);
68 cairo_surface_destroy(image);
75 // ./main --task hit_shape.task 0 --action-mode=random --nb-ticks=5000 --proportion-for-training=0.5 --save-file=dump.mem --no-window
78 // ./main --task hit_shape.task 0 --action-mode=intelligent --load-file=dump.mem
80 //////////////////////////////////////////////////////////////////////
82 void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
83 if(n_opt + n >= argc) {
84 cerr << "Missing argument for " << argv[n_opt] << "." << endl;
85 cerr << "Expecting " << help << "." << endl;
90 void print_help_and_exit(int e) {
91 cout << "Arguments:" << endl;
92 cout << " --no-window" << endl;
93 cout << " --nb-ticks=<int: number of ticks>" << endl;
94 cout << " --nb-training-iterations=<int: number of training iterations>" << endl;
95 cout << " --load-file=<filename: dump file>" << endl;
96 cout << " --save-file=<filename: dump file>" << endl;
97 cout << " --proportion-for-training=<float: proportion of samples for training>" << endl;
98 cout << " --action-mode=<idle|random|intelligent>" << endl;
99 cout << " --task <filename: task to load> <int: degree>" << endl;
103 //////////////////////////////////////////////////////////////////////
105 int main(int argc, char **argv) {
107 const int buffer_size = 256;
108 char intelligence_load_file[buffer_size] = "", intelligence_save_file[buffer_size] = "";
113 int nb_ticks = 10000;
114 int nb_training_iterations = 10;
115 scalar_t proportion_for_training = -1;
117 Polygon *grabbed_polygon = 0;
118 scalar_t relative_grab_x = 0, relative_grab_y = 0;
121 bool press_shift = false;
122 enum { IDLE, RANDOM, INTELLIGENT } action_mode = IDLE;
123 bool got_event = true;
124 int current_action = 0;
126 scalar_t last_hand_x = 0, last_hand_y = 0;
127 Polygon *last_grabbing = 0;
128 bool no_window = false;
130 //////////////////////////////////////////////////////////////////////
131 // Parsing the shell arguments
132 //////////////////////////////////////////////////////////////////////
136 if(argc == 1 || strcmp(argv[i], "--help") == 0) print_help_and_exit(0);
138 else if(strcmp(argv[i], "--test") == 0) {
143 else if(strcmp(argv[i], "--task") == 0) {
144 check_opt(argc, argv, i, 2, "<filename: task to load> <int: degree>");
146 cerr << "Can not load two tasks." << endl;
149 task = load_task(argv[i+1]);
150 task_degree = atoi(argv[i+2]);
153 } else if(strncmp(argv[i], "--", 2) == 0) {
154 char variable_name[buffer_size] = "", variable_value[buffer_size] = "";
155 char *o = argv[i]+2, *s = variable_name, *u = variable_value;
156 while(*o && *o != '=') *s++ = *o++;
159 while(*o) *u++ = *o++;
162 if(strcmp(variable_name, "nb-ticks") == 0)
163 nb_ticks = atoi(variable_value);
164 else if(strcmp(variable_name, "nb-training-iterations") == 0)
165 nb_training_iterations = atoi(variable_value);
166 else if(strcmp(variable_name, "proportion-for-training") == 0)
167 proportion_for_training = atof(variable_value);
168 else if(strcmp(variable_name, "no-window") == 0)
170 else if(strcmp(variable_name, "save-file") == 0)
171 strcpy(intelligence_save_file, variable_value);
172 else if(strcmp(variable_name, "load-file") == 0)
173 strcpy(intelligence_load_file, variable_value);
174 else if(strcmp(variable_name, "action-mode") == 0) {
175 if(strcmp(variable_value, "idle") == 0) action_mode = IDLE;
176 else if(strcmp(variable_value, "random") == 0) action_mode = RANDOM;
177 else if(strcmp(variable_value, "intelligent") == 0) action_mode = INTELLIGENT;
179 cerr << "The only known modes are idle, random and intelligent" << endl;
183 cerr << "Unknown option " << argv[i] << endl;
184 print_help_and_exit(1);
188 cerr << "Unknown option " << argv[i] << endl;
189 print_help_and_exit(1);
193 cout << "FlatLand, a toy universe for goal-planning experiments." << endl;
196 task = load_task("dummy.so");
200 cout << "Loaded task " << task->name()
201 << " with degree " << task_degree << "/" << task->nb_degrees()
204 if(task_degree < 0 || task_degree >= task->nb_degrees()) {
205 cout << "Invalid degree: " << task_degree << "." << endl;
209 //////////////////////////////////////////////////////////////////////
210 // Various initializations
211 //////////////////////////////////////////////////////////////////////
213 Universe universe(100, task->width(), task->height());
214 task->init(&universe, task_degree);
215 Retina retina(&universe);
216 Manipulator manipulator(task);
217 manipulator.force_move(task->width()/2, task->height()/2);
218 retina.set_location(manipulator.hand_x(), manipulator.hand_y());
220 SimpleWindow *window_main = 0, *window_brain = 0;
222 cairo_t *cairo_cr = 0;
225 int window_main_fd = -1;
227 MapConcatener sensory_map(2);
228 sensory_map.add_map(&retina);
229 sensory_map.add_map(&manipulator);
232 MapExpander expanded_map(1000);
233 expanded_map.set_input(&sensory_map);
236 Intelligence intelligence(&expanded_map, &manipulator, nb_ticks + 1, nb_training_iterations);
237 intelligence.update(0, 0.0);
239 if(intelligence_load_file[0]) {
240 cout << "Loading from " << intelligence_load_file << " ... " ;
242 ifstream in(intelligence_load_file);
244 cerr << "error reading " << intelligence_load_file << "." << endl;
247 intelligence.load(in);
248 cout << "done." << endl ;
252 cout << "Started without windows." << endl;
254 window_main = new SimpleWindow("Universe (main window)", 4, 4, task->width(), task->height());
255 window_main_fd = window_main->file_descriptor();
258 cairo_cr = window_main->get_cairo_context_resource();
260 cout << "When the main window has the focus, press `q' to quit and click and drag to move" << endl
261 << "objects." << endl;
262 window_brain = new SimpleWindow("Universe (brain)",
263 12 + task->width(), 4,
264 retina.width(), retina.height() + manipulator.parameter_height());
270 scalar_t sum_reward = 0;
272 //////////////////////////////////////////////////////////////////////
274 //////////////////////////////////////////////////////////////////////
276 while(!quit && tick != nb_ticks) {
285 FD_SET (window_main_fd, &fds);
287 tv.tv_usec = 5000; // 0.05s
288 r = select(window_main_fd + 1, &fds, 0, 0, &tv);
295 cout << tick << " " << sum_reward << " \r"; cout.flush();
298 if(r == 0) { // No window event, thus it's the clock tick
302 bool changed = got_event;
305 switch(action_mode) {
309 current_action = manipulator.random_action();
312 current_action = intelligence.best_action();
313 // if(drand48() < 0.5) current_action = intelligence.best_action();
314 // else current_action = manipulator.random_action();
318 manipulator.do_action(current_action);
320 scalar_t dt = 1.0/scalar_t(nb_it);
321 for(int k = 0; k < nb_it; k++) {
322 manipulator.update(dt, &universe);
323 task->update(dt, &universe, &manipulator);
324 changed |= universe.update(dt);
329 changed |= manipulator.hand_x() != last_hand_x ||
330 manipulator.hand_y() != last_hand_y ||
331 manipulator.grabbing() != last_grabbing;
333 scalar_t reward = task->reward(&universe, &manipulator);
334 sum_reward += abs(reward);
335 intelligence.update(current_action, reward);
336 expanded_map.update_map();
339 last_hand_x = manipulator.hand_x();
340 last_hand_y = manipulator.hand_y();
341 last_grabbing = manipulator.grabbing();
343 retina.set_location(manipulator.hand_x(),
344 manipulator.hand_y());
347 window_main->color(0.0, 0.0, 0.0);
348 window_main->color(1.0, 1.0, 1.0);
350 task->draw(window_main);
353 universe.draw(cairo_cr);
355 universe.draw(window_main);
358 manipulator.draw_on_universe(window_main);
359 retina.draw_on_universe(window_main);
361 if(grabbed_polygon) {
363 x = int(grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y));
364 y = int(grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y));
365 window_main->color(0.0, 0.0, 0.0);
366 window_main->draw_line(x - delta, y, x + delta, y);
367 window_main->draw_line(x, y - delta, x, y + delta);
373 retina.draw_parameters(0, 0, window_brain);
374 manipulator.draw_parameters(0, retina.height() + 1, window_brain);
375 window_brain->show();
380 } else if(r > 0) { // We got window events, let's process them
384 if(FD_ISSET(window_main_fd, &fds)) {
389 se = window_main->event();
393 case SimpleEvent::MOUSE_CLICK_PRESS:
399 manipulator.force_move(se.x, se.y);
400 manipulator.do_action(Manipulator::ACTION_GRAB);
402 grabbed_polygon = universe.pick_polygon(se.x, se.y);
403 if(grabbed_polygon) {
404 relative_grab_x = grabbed_polygon->relative_x(se.x, se.y);
405 relative_grab_y = grabbed_polygon->relative_y(se.x, se.y);
411 Polygon *g = universe.pick_polygon(se.x, se.y);
412 if(g) g->_theta += M_PI/32;
417 Polygon *g = universe.pick_polygon(se.x, se.y);
418 if(g) g->_theta -= M_PI/32;
425 case SimpleEvent::MOUSE_CLICK_RELEASE:
428 if(press_shift) manipulator.do_action(Manipulator::ACTION_RELEASE);
429 else grabbed_polygon = 0;
435 case SimpleEvent::MOUSE_MOTION:
438 manipulator.force_move(se.x, se.y);
439 } else if(grabbed_polygon) {
440 scalar_t xf, yf, force_x, force_y, f, fmax = 100;
441 xf = grabbed_polygon->absolute_x(relative_grab_x, relative_grab_y);
442 yf = grabbed_polygon->absolute_y(relative_grab_x, relative_grab_y);
445 f = sqrt(sq(force_x) + sq(force_y));
446 if(f > fmax) { force_x = (force_x * fmax)/f; force_y = (force_y * fmax)/f; }
447 grabbed_polygon->apply_force(0.1, xf, yf, force_x, force_y);
453 case SimpleEvent::KEY_PRESS:
455 if(strcmp(se.key, "q") == 0) {
459 else if(strcmp(se.key, "s") == 0) {
461 retina.save_as_ppm("/tmp/retina.ppm");
462 cout << "Retina screen shot saved in /tmp/retina.ppm" << endl;
465 XFigTracer tracer("/tmp/universe.fig");
466 universe.print_xfig(&tracer);
471 FILE *file = fopen("/tmp/universe.png", "w");
472 generate_png(&universe, 0.25, file);
473 // generate_png(task->width(), task->height(), &universe, "/tmp/universe.png");
474 cout << "Universe image saved in /tmp/universe.png" << endl;
480 else if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) {
484 else if(strcmp(se.key, "Up") == 0) {
485 manipulator.do_action(Manipulator::ACTION_MOVE_UP);
488 else if(strcmp(se.key, "Right") == 0) {
489 manipulator.do_action(Manipulator::ACTION_MOVE_RIGHT);
492 else if(strcmp(se.key, "Down") == 0) {
493 manipulator.do_action(Manipulator::ACTION_MOVE_DOWN);
496 else if(strcmp(se.key, "Left") == 0) {
497 manipulator.do_action(Manipulator::ACTION_MOVE_LEFT);
500 else if(strcmp(se.key, "g") == 0) {
501 manipulator.do_action(Manipulator::ACTION_GRAB);
504 else if(strcmp(se.key, "r") == 0) {
505 manipulator.do_action(Manipulator::ACTION_RELEASE);
508 else if(strcmp(se.key, "space") == 0) {
509 switch(action_mode) {
511 action_mode = RANDOM;
512 cout << "Switched to random mode" << endl;
515 action_mode = INTELLIGENT;
516 cout << "Switched to intelligent mode" << endl;
519 cout << "Switched to idle mode" << endl;
525 else cout << "Undefined key " << se.key << endl;
529 case SimpleEvent::KEY_RELEASE:
531 if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) press_shift = false;
539 } while(se.type != SimpleEvent::NO_EVENT);
541 cerr << "Error on select: " << strerror(errno) << endl;
547 if(proportion_for_training > 0) {
548 cout << "Learning ... "; cout.flush();
549 intelligence.learn(proportion_for_training);
550 cout << "done." << endl;
553 if(intelligence_save_file[0]) {
554 cout << "Saving to " << intelligence_save_file << endl; cout.flush();
555 ofstream os(intelligence_save_file);
557 cerr << "error writing " << intelligence_save_file << "." << endl;
560 cout << "done." << endl;
561 intelligence.save(os);