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. //
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. //
12 // Written and (C) by François Fleuret //
13 // Contact <francois.fleuret@epfl.ch> for comments & bug reports //
14 ////////////////////////////////////////////////////////////////////////////////
29 #include "simple_window.h"
32 #include "manipulator.h"
33 #include "intelligence.h"
36 // ./main --task hit_shape.task 0 --action-mode=random --nb-ticks=5000 --proportion-for-training=0.5 --save-file=dump.mem --no-window
39 // ./main --task hit_shape.task 0 --action-mode=intelligent --load-file=dump.mem
41 //////////////////////////////////////////////////////////////////////
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;
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;
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;
66 //////////////////////////////////////////////////////////////////////
68 int main(int argc, char **argv) {
70 const int buffer_size = 256;
71 char intelligence_load_file[buffer_size] = "", intelligence_save_file[buffer_size] = "";
77 int nb_training_iterations = 10;
78 scalar_t proportion_for_training = -1;
80 Polygon *grabbed_polygon = 0;
81 scalar_t relative_grab_x = 0, relative_grab_y = 0;
84 bool press_shift = false;
85 enum { IDLE, RANDOM, INTELLIGENT } action_mode = IDLE;
86 bool got_event = true;
87 int current_action = 0;
89 scalar_t last_hand_x = 0, last_hand_y = 0;
90 Polygon *last_grabbing = 0;
91 bool no_window = false;
93 //////////////////////////////////////////////////////////////////////
94 // Parsing the shell arguments
95 //////////////////////////////////////////////////////////////////////
99 if(argc == 1 || strcmp(argv[i], "--help") == 0) print_help_and_exit(0);
101 else if(strcmp(argv[i], "--test") == 0) {
106 else if(strcmp(argv[i], "--task") == 0) {
107 check_opt(argc, argv, i, 2, "<filename: task to load> <int: degree>");
109 cerr << "Can not load two tasks." << endl;
112 task = load_task(argv[i+1]);
113 task_degree = atoi(argv[i+2]);
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++;
122 while(*o) *u++ = *o++;
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)
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;
142 cerr << "The only known modes are idle, random and intelligent" << endl;
146 cerr << "Unknown option " << argv[i] << endl;
147 print_help_and_exit(1);
151 cerr << "Unknown option " << argv[i] << endl;
152 print_help_and_exit(1);
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;
160 task = load_task("dummy.task");
164 cout << "Loaded task " << task->name()
165 << " with degree " << task_degree << "/" << task->nb_degrees()
168 if(task_degree < 0 || task_degree >= task->nb_degrees()) {
169 cout << "Invalid degree: " << task_degree << "." << endl;
173 //////////////////////////////////////////////////////////////////////
174 // Various initializations
175 //////////////////////////////////////////////////////////////////////
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());
184 SimpleWindow *window_main = 0, *window_brain = 0;
188 MapConcatener sensory_map(2);
189 sensory_map.add_map(&retina);
190 sensory_map.add_map(&manipulator);
193 MapExpander expanded_map(1000);
194 expanded_map.set_input(&sensory_map);
197 Intelligence intelligence(&expanded_map, &manipulator, nb_ticks + 1, nb_training_iterations);
198 intelligence.update(0, 0.0);
200 if(intelligence_load_file[0]) {
201 cout << "Loading from " << intelligence_load_file << " ... " ;
203 ifstream in(intelligence_load_file);
205 cerr << "error reading " << intelligence_load_file << "." << endl;
208 intelligence.load(in);
209 cout << "done." << endl ;
213 window_main = new SimpleWindow("Universe (main window)", 4, 4, task->width(), task->height());
214 winfd = window_main->file_descriptor();
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());
223 cout << "Started without windows." << endl;
228 scalar_t sum_reward = 0;
230 //////////////////////////////////////////////////////////////////////
232 //////////////////////////////////////////////////////////////////////
234 while(!quit && tick != nb_ticks) {
243 FD_SET (winfd, &fds);
245 tv.tv_usec = 5000; // 0.05s
246 r = select(winfd + 1, &fds, 0, 0, &tv);
253 cout << tick << " " << sum_reward << " \r"; cout.flush();
256 if(r == 0) { // No window event, thus it's the clock tick
260 bool changed = got_event;
263 switch(action_mode) {
267 current_action = manipulator.random_action();
270 current_action = intelligence.best_action();
271 // if(drand48() < 0.5) current_action = intelligence.best_action();
272 // else current_action = manipulator.random_action();
276 manipulator.do_action(current_action);
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);
287 changed |= manipulator.hand_x() != last_hand_x ||
288 manipulator.hand_y() != last_hand_y ||
289 manipulator.grabbing() != last_grabbing;
291 scalar_t reward = task->reward(&universe, &manipulator);
292 sum_reward += abs(reward);
293 intelligence.update(current_action, reward);
294 expanded_map.update_map();
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());
303 window_main->color(0.0, 0.0, 0.0);
305 task->draw(window_main);
306 universe.draw(window_main);
307 manipulator.draw_on_universe(window_main);
308 retina.draw_on_universe(window_main);
310 if(grabbed_polygon) {
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);
322 retina.draw_parameters(0, 0, window_brain);
323 manipulator.draw_parameters(0, retina.height() + 1, window_brain);
324 window_brain->show();
329 } else if(r > 0) { // We got window events, let's process them
333 if(FD_ISSET(winfd, &fds)) {
338 se = window_main->event();
342 case SimpleEvent::MOUSE_CLICK_PRESS:
348 manipulator.force_move(se.x, se.y);
349 manipulator.do_action(Manipulator::ACTION_GRAB);
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);
360 Polygon *g = universe.pick_polygon(se.x, se.y);
361 if(g) g->_theta += M_PI/32;
366 Polygon *g = universe.pick_polygon(se.x, se.y);
367 if(g) g->_theta -= M_PI/32;
374 case SimpleEvent::MOUSE_CLICK_RELEASE:
377 if(press_shift) manipulator.do_action(Manipulator::ACTION_RELEASE);
378 else grabbed_polygon = 0;
384 case SimpleEvent::MOUSE_MOTION:
386 if(press_shift) manipulator.force_move(se.x, se.y);
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);
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);
403 case SimpleEvent::KEY_PRESS:
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;
410 ofstream out("/tmp/universe.fig");
411 universe.print_fig(out);
414 else if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) press_shift = true;
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);
423 else if(strcmp(se.key, "space") == 0) {
424 switch(action_mode) {
426 action_mode = RANDOM;
427 cout << "Switched to random mode" << endl;
430 action_mode = INTELLIGENT;
431 cout << "Switched to intelligent mode" << endl;
434 cout << "Switched to idle mode" << endl;
440 else cout << "Undefined key " << se.key << endl;
444 case SimpleEvent::KEY_RELEASE:
446 if(strcmp(se.key, "Shift_L") == 0 || strcmp(se.key, "Shift_R") == 0) press_shift = false;
454 } while(se.type != SimpleEvent::NO_EVENT);
456 cerr << "Error on select: " << strerror(errno) << endl;
462 if(proportion_for_training > 0) {
463 cout << "Learning ... "; cout.flush();
464 intelligence.learn(proportion_for_training);
465 cout << "done." << endl;
468 if(intelligence_save_file[0]) {
469 cout << "Saving to " << intelligence_save_file << endl; cout.flush();
470 ofstream os(intelligence_save_file);
472 cerr << "error writing " << intelligence_save_file << "." << endl;
475 cout << "done." << endl;
476 intelligence.save(os);