1 /* Copyright (C) 1998 Jim Hall <jhall1@isd.net>
2 * Copyright (C) 2008 Bradley Smith <brad@brad-smith.co.uk>
3 *
4 * GNU Robots game engine. This is the main() program, using GNU
5 * Guile as the backend to handle the language.
6 *
7 * GNU Robots is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * GNU Robots is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Robots. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <stdio.h>
22 #include <unistd.h> /* for getopt */
23 #include <string.h> /* for strdup */
24
25 #include <glib.h>
26 #include <glib/gprintf.h>
27
28 #include <libguile.h>
29
30 #include <getopt.h> /* for GNU getopt_long */
31
32 #include "grobot.h" /* the robot structure, and robot manipulation
33 routines */
34
35 #include "api.h" /* robot API, as Scheme functions */
36 #include "ui-cmdwin.h"
37 #include "ui-arena.h"
38 #include "ui-window.h"
39 #include "ui-window-private.h"
40 #include "config.h"
41 #include "configs.h"
42 #include "map.h" /* Game Map */
43 #include "main.h" /* for this source file */
44
45 #define BUFF_LEN 1024
46
47 /* Globals (share with api.c) */
48 GList *robots = NULL;
49 GRobot *robot = NULL; /* The current robot */
50 UIWindow *ui;
51 UIArena *arena;
52 Map *map;
53
54 gpointer callback(gpointer data);
55 SCM catch_handler(void *data, SCM tag, SCM throw_args);
56 gint is_file_readable(const gchar * filename);
57
58 /************************************************************************
59 * gint main(gint argc, gchar *argv[]) *
60 * *
61 * Entry point *
62 ************************************************************************/
main(gint argc,gchar * argv[])63 gint main(gint argc, gchar *argv[])
64 {
65 gint opt; /* the option read from getopt */
66 gint flag; /* flag passed back from getopt - NOT USED */
67
68 gchar maps_path[MAX_PATH], scripts_path[MAX_PATH];
69
70 gchar *main_argv[5] = { "GNU Robots",
71 NULL,
72 NULL,
73 NULL,
74 NULL
75 };
76
77 struct option long_opts[] = {
78 {"version", 0, NULL, 'V'},
79 {"help", 0, NULL, 'h'},
80 {"map-file", 1, NULL, 'f'},
81 {"shields", 1, NULL, 's'},
82 {"energy", 1, NULL, 'e'},
83 {NULL, 0, NULL, 0}
84 };
85
86 /* Initialize the GType system first */
87 g_type_init();
88
89 /* Check command line */
90
91 /* Create a robot Object */
92 robot = g_robot_new(1, 1, 1, 0, DEFAULT_ENERGY, DEFAULT_SHIELDS, 0, 0,
93 NULL, NULL);
94
95 g_assert(robot != NULL);
96
97 /* And add to to the list of robots */
98 robots = g_list_append(robots, robot);
99
100 while ((opt = getopt_long(argc, argv, "Vhf:s:e:p:", long_opts,
101 &flag)) != EOF)
102 {
103 switch (opt)
104 {
105 case 'V':
106 /* Display version, then quit */
107 g_printf("%s\n\n", PKGINFO);
108 g_printf("%s\n\n", COPYRIGHT);
109 g_printf
110 ("GNU Robots is free software: you can redistribute it and/or modify\n"
111 "it under the terms of the GNU General Public License as published by\n"
112 "the Free Software Foundation, either version 3 of the License, or\n"
113 "(at your option) any later version.\n");
114 g_printf
115 ("\nGNU Robots is distributed in the hope that it will be useful,\n"
116 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
117 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
118 "GNU General Public License for more details.\n");
119 g_printf
120 ("\nYou should have received a copy of the GNU General Public License\n"
121 "along with GNU Robots. If not, see <http://www.gnu.org/licenses/>.\n");
122
123 exit(0);
124 break;
125
126 case 'h':
127 /* Display help, then quit. */
128 usage(argv[0]);
129 exit(0);
130 break;
131
132 case 'f':
133 /* Set map file */
134 main_argv[1] = optarg; /* pointer assignment */
135 break;
136
137 case 's':
138 /* Set shields */
139 robot->shields = (glong) atol(optarg);
140 break;
141
142 case 'e':
143 /* Set energy */
144 robot->energy = (glong) atol(optarg);
145 break;
146
147 default:
148 /* invalid option */
149 usage(argv[0]);
150 exit(1);
151 break;
152 } /* switch */
153 } /* while */
154
155 /* Extra arg is the Scheme file */
156
157 if (optind < argc)
158 {
159 /* Set Scheme file */
160 main_argv[2] = argv[optind]; /* pointer assignment */
161 }
162
163 /* Check that files have been given */
164 if (main_argv[1] == NULL)
165 {
166 main_argv[1] = g_malloc(MAX_PATH);
167 g_strlcpy(main_argv[1], DEFAULT_MAP, MAX_PATH);
168 g_fprintf(stderr, "map file not specified, trying default: %s\n",
169 main_argv[1]);
170 }
171
172 /* Check that files exist */
173 g_strlcpy(maps_path, main_argv[1], MAX_PATH);
174
175 if (!is_file_readable(maps_path))
176 {
177 g_strlcpy(maps_path, MAPS_PATH, MAX_PATH);
178 g_strlcat(maps_path, "/", MAX_PATH);
179 g_strlcat(maps_path, main_argv[1], MAX_PATH);
180
181 if (!is_file_readable(maps_path))
182 {
183 gchar *env = getenv(MAPS_PATH_ENV);
184
185 if (env != NULL)
186 {
187 g_strlcpy(maps_path, env, MAX_PATH);
188 g_strlcat(maps_path, "/", MAX_PATH);
189 g_strlcat(maps_path, main_argv[1], MAX_PATH);
190
191 if (!is_file_readable(maps_path))
192 {
193 g_fprintf(stderr,
194 "%s: %s: game map file does not exist or is not readable\n",
195 argv[0], main_argv[1]);
196 exit(1);
197 }
198 }
199 else
200 {
201 g_fprintf(stderr,
202 "%s: %s: game map file does not exist or is not readable\n",
203 argv[0], main_argv[1]);
204 exit(1);
205 }
206 }
207 }
208
209 main_argv[1] = maps_path;
210
211 /* Now the Scheme file */
212 if (main_argv[2] != NULL)
213 {
214 g_strlcpy(scripts_path, main_argv[2], MAX_PATH);
215
216 if (!is_file_readable(scripts_path))
217 {
218 g_strlcpy(scripts_path, SCRIPTS_PATH, MAX_PATH);
219 g_strlcat(scripts_path, "/", MAX_PATH);
220 g_strlcat(scripts_path, main_argv[2], MAX_PATH);
221
222 if (!is_file_readable(scripts_path))
223 {
224 gchar *env = getenv(SCRIPTS_PATH_ENV);
225
226 if (env != NULL)
227 {
228 g_strlcpy(scripts_path, env, MAX_PATH);
229 g_strlcat(scripts_path, "/", MAX_PATH);
230 g_strlcat(scripts_path, main_argv[2], MAX_PATH);
231
232 if (!is_file_readable(scripts_path))
233 {
234 g_fprintf(stderr,
235 "%s: %s: Scheme file does not exist or is not readable\n",
236 argv[0], main_argv[2]);
237 exit(1);
238 }
239 }
240 else
241 {
242 g_fprintf(stderr,
243 "%s: %s: Scheme file does not exist or is not readable\n",
244 argv[0], main_argv[2]);
245 exit(1);
246 }
247 }
248 }
249
250 main_argv[2] = scripts_path;
251 }
252 else
253 {
254 /* argv[2] can't be NULL as argv[3] may also be NULL */
255 main_argv[2] = "";
256 }
257
258 /* Start Guile environment. Does not exit */
259 g_printf("%s\n", PKGINFO);
260 g_printf("%s\n", COPYRIGHT);
261 g_printf("GNU Robots comes with ABSOLUTELY NO WARRANTY\n");
262 g_printf
263 ("This is free software, and you are welcome to redistribute it\n");
264 g_printf
265 ("under certain conditions; see the file `COPYING' for details.\n");
266 g_printf("Loading Guile ... Please wait\n\n");
267
268 scm_boot_guile(3, main_argv, main_prog, NULL);
269
270 return 0; /* never gets here, but keeps compiler happy */
271 }
272
273 /************************************************************************
274 * void main_prog(void *closure, gint argc, gchar *argv[]) *
275 * *
276 * The main program code that is executed after Guile starts up. Pass *
277 * the Scheme program as argv[1] and the map file as argv[2]. The *
278 * program name is still argv[0]. *
279 ************************************************************************/
main_prog(void * closure,gint argc,gchar * argv[])280 void main_prog(void *closure, gint argc, gchar *argv[])
281 {
282 gchar *map_file = argv[1];
283 gchar *robot_program = argv[2];
284 gboolean loading = TRUE;
285
286 api_init();
287
288 g_printf("Map file: %s\n", map_file);
289
290 map = map_new_from_file(map_file, DEFAULT_MAP_ROWS,
291 DEFAULT_MAP_COLUMNS);
292
293 if (map == NULL)
294 {
295 exit_nicely();
296 }
297
298 g_thread_init(NULL);
299 gdk_threads_init();
300 g_thread_create(callback, &loading, FALSE, NULL);
301
302 /* ensure the robot is placed properly */
303 MAP_SET_OBJECT(map, G_ROBOT_POSITION_Y(robot),
304 G_ROBOT_POSITION_X(robot), ROBOT);
305
306 g_printf("Loading GTK Interface ... Please wait\n\n");
307 while(loading);
308
309 /* Now initialize the rest of the Robot properties */
310 g_object_set(G_OBJECT(robot),
311 "user-interface", G_OBJECT(arena), "map", G_OBJECT(map), NULL);
312
313 g_signal_connect(G_OBJECT(robot), "death", G_CALLBACK(death), NULL);
314
315 if (strlen(robot_program) != 0)
316 {
317 /* execute a Scheme file */
318 g_printf("Robot program: %s\n", robot_program);
319 scm_c_primitive_load(robot_program);
320 }
321 else
322 {
323 gchar buf[BUFF_LEN];
324
325 g_printf
326 ("Robot program not specified. Entering interactive mode..\n");
327
328 while (1)
329 {
330 memset(&buf, 0, BUFF_LEN);
331
332 ui_cmdwin_get_string(UI_CMDWIN(ui->priv->cmdwin), "guile> ",
333 buf, BUFF_LEN);
334
335 scm_internal_catch(SCM_BOOL_T,
336 (scm_t_catch_body) scm_c_eval_string, buf,
337 catch_handler, NULL);
338 }
339 }
340
341 /* done */
342 exit_nicely();
343 }
344
callback(gpointer data)345 gpointer callback(gpointer data)
346 {
347 gtk_init(0, NULL);
348
349 ui = UI_WINDOW(ui_window_new());
350 ui_window_postinit(ui, map);
351
352 arena = UI_ARENA(ui->priv->arena);
353
354 /* draw the map */
355 ui_arena_draw(arena);
356 ui_arena_update_status(arena, "Welcome to GNU Robots", -1, -1, -1);
357
358 *(gboolean*)data = FALSE;
359
360 gdk_threads_enter();
361 gtk_main();
362 gdk_threads_leave();
363
364 return NULL;
365 }
366
367 /************************************************************************
368 * SCM catch_handler (void *data, SCM tag, SCM throw_args); *
369 * *
370 * Responsible for handling errors *
371 ************************************************************************/
catch_handler(void * data,SCM tag,SCM throw_args)372 SCM catch_handler(void *data, SCM tag, SCM throw_args)
373 {
374
375 /*
376 gchar *message = "Couldn't get error message\n";
377
378 if (scm_ilength (throw_args) > 1
379 && SCM_NFALSEP (scm_string_p (SCM_CADR (throw_args))))
380 {
381 message = SCM_STRING_CHARS (SCM_CADR (throw_args));
382 }
383
384 else if (SCM_NFALSEP (scm_symbol_p (tag)))
385 {
386 message = SCM_SYMBOL_CHARS (tag);
387 }
388
389 user_interface_update_status (ui, message, -1, -1, -1);
390 */
391
392 g_printf("Invalid Instruction\n");
393
394 return SCM_BOOL_F;
395 }
396
death(GRobot * robot)397 void death(GRobot *robot)
398 {
399 /* We get a ref increment on a signal */
400 g_object_unref(G_OBJECT(robot));
401
402 exit_nicely();
403 }
404
405 /************************************************************************
406 * void exit_nicely() *
407 * *
408 * A function that allows the program to exit nicely, after freeing all *
409 * memory pointers, etc. *
410 ************************************************************************/
exit_nicely()411 void exit_nicely()
412 {
413 glong score, energy, shields, shots, units;
414
415 /* Stop the UI */
416 if (ui != NULL)
417 {
418 g_object_unref(G_OBJECT(ui));
419 }
420
421 /* Get rid of the map object */
422 if (map != NULL)
423 {
424 g_object_unref(G_OBJECT(map));
425 }
426
427 /* Show statistics */
428 g_object_get(G_OBJECT(robot),
429 "shields", &shields,
430 "energy", &energy,
431 "units", &units, "shots", &shots, "score", &score, NULL);
432
433 g_list_foreach(robots, (GFunc) g_object_unref, NULL);
434 g_list_free(robots);
435
436 g_printf(
437 "\n-----------------------STATISTICS-----------------------\n");
438 g_printf("Shields: %ld\n", (shields < 0 ? 0 : shields));
439 g_printf("Energy: %ld\n", (energy < 0 ? 0 : energy));
440 g_printf("Units walked: %ld\n", (units < 0 ? 0 : units));
441 g_printf("Shots: %ld\n", (shots < 0 ? 0 : shots));
442 g_printf("Score: %ld\n", score);
443
444 /* Show results, if any */
445 if (shields < 1)
446 {
447 g_printf("** Robot took too much damage, and died.\n");
448 }
449
450 else if (energy < 1)
451 {
452 g_printf("** Robot ran out of energy.\n");
453 }
454
455 /* Quit program */
456 exit(0);
457 }
458
459 /************************************************************************
460 * void usage(const gchar *argv0) *
461 * *
462 * A function that prints the usage of GNU Robots to the user. Assume *
463 * text mode for this function. We have not initialized X Windows yet. *
464 ************************************************************************/
usage(const gchar * argv0)465 void usage(const gchar *argv0)
466 {
467 g_printf("%s\n", PKGINFO);
468 g_printf("%s\n", COPYRIGHT);
469 g_printf
470 ("Game/diversion where you construct a program for a little robot\n");
471 g_printf
472 ("then set him loose and watch him explore a world on his own.\n\n");
473
474 g_printf("Usage: %s [OPTION]... [FILE]\n\n", argv0);
475 g_printf(" -f, --map-file=FILE Load map file\n");
476 g_printf(" -s, --shields=N Set initial shields to N\n");
477 g_printf(" -e, --energy=N Set initial energy to N\n");
478 g_printf
479 (" -V, --version Output version information and exit\n");
480 g_printf(" -h, --help Display this help and exit\n");
481 g_printf("\nNote: FILE refers to a scheme file and %s enters into \n",
482 argv0);
483 g_printf(" interactive mode if it is not specified.\n");
484
485 g_printf("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
486 }
487
488 /************************************************************************
489 * gint is_file_readable (const gchar *filename) *
490 * *
491 * Checks if a file is a readable file. We will use this function as *
492 * part of a sanity check, before we get anywhere near having to open *
493 * files. This will save on error checking later on, when we may have *
494 * already initialized another environment (Curses, X Windows, ...) *
495 ************************************************************************/
is_file_readable(const gchar * filename)496 gint is_file_readable(const gchar *filename)
497 {
498 FILE *stream;
499
500 stream = fopen(filename, "r");
501 if (stream == NULL)
502 {
503 /* Failed */
504 return (0);
505 }
506
507 /* Success */
508 fclose(stream);
509 return (1);
510 }
511