[gnome-games/gnibbles-clutter] Added a bunch of code from worm.c to worm-clutter.c related to AI
- From: Guillaume Béland <guillaubel src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-games/gnibbles-clutter] Added a bunch of code from worm.c to worm-clutter.c related to AI
- Date: Wed, 3 Jun 2009 20:37:26 -0400 (EDT)
commit c9dcede0f36f5e05b493e9623587d4ce7919fd18
Author: Guillaume Beland <guillaume beland gmail com>
Date: Wed Jun 3 19:57:02 2009 -0400
Added a bunch of code from worm.c to worm-clutter.c related to AI
Most of this code shouldn't change, the rest will and has started to be adapted
for the new code.
---
gnibbles/board.h | 10 +-
gnibbles/main.c | 10 +-
gnibbles/worm-clutter.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++-
gnibbles/worm-clutter.h | 9 +-
4 files changed, 544 insertions(+), 10 deletions(-)
diff --git a/gnibbles/board.h b/gnibbles/board.h
index 6851b91..defc148 100644
--- a/gnibbles/board.h
+++ b/gnibbles/board.h
@@ -28,11 +28,11 @@
#include "level.h"
typedef struct {
- gint width;
- gint height;
- GtkWidget *clutter_widget;
- ClutterActor *surface;
- ClutterActor *level;
+ gint width;
+ gint height;
+ GtkWidget *clutter_widget;
+ ClutterActor *surface;
+ ClutterActor *level;
} GnibblesBoard;
GnibblesBoard* gnibbles_board_new (gint t_w, gint t_h);
diff --git a/gnibbles/main.c b/gnibbles/main.c
index be9fc12..cd04265 100644
--- a/gnibbles/main.c
+++ b/gnibbles/main.c
@@ -86,6 +86,8 @@ extern GdkPixbuf *logo_pixmap;
GnibblesProperties *properties;
+GnibblesLevel *level;
+
GnibblesScoreboard *scoreboard;
GdkPixbuf *wall_pixmaps[11] = { NULL, NULL, NULL, NULL, NULL,
@@ -1329,11 +1331,13 @@ main (int argc, char **argv)
int i;
- gnibbles_board_load_level (board, gnibbles_level_new (1));
+ level = gnibbles_level_new (1);
+
+ gnibbles_board_load_level (board, level);
for (i = 0; i < properties->numworms; i++) {
- clutter_container_add_actor (CLUTTER_CONTAINER (stage), cworms[i]->actors);
- clutter_actor_raise_top (cworms[i]->actors);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), cworms[i]->actors);
+ clutter_actor_raise_top (cworms[i]->actors);
}
//render_logo_clutter (board);
diff --git a/gnibbles/worm-clutter.c b/gnibbles/worm-clutter.c
index 4614793..32224a5 100644
--- a/gnibbles/worm-clutter.c
+++ b/gnibbles/worm-clutter.c
@@ -21,7 +21,7 @@
*/
#include <config.h>
#include <glib/gprintf.h>
-
+#include <ctype.h>
#include <glib/gi18n.h>
#include <gdk/gdk.h>
#include <stdlib.h>
@@ -29,11 +29,21 @@
#include <clutter-gtk/clutter-gtk.h>
#include "main.h"
#include "gnibbles.h"
+#include "level.h"
+#include "boni.h"
+#include "bonus.h"
+#include "warpmanager.h"
#include "properties.h"
+
#include "worm-clutter.h"
extern GnibblesProperties *properties;
extern GdkPixbuf *worm_pixmaps[];
+extern GnibblesLevel *level;
+extern GnibblesBoni *boni;
+extern GnibblesWarpManager *warpmanager;
+extern GnibblesCWorm *cworms[NUMWORMS];
+
GnibblesCWorm*
gnibbles_cworm_new (guint number, guint t_xhead,
@@ -229,3 +239,516 @@ gnibbles_cworm_resize (GnibblesCWorm *worm, gint newtile)
}
}
+
+
+
+void
+gnibbles_cworm_draw_head (GnibblesCWorm * worm)
+{
+ //worm->keypress = 0;
+
+ switch (worm->direction) {
+ case WORMUP:
+ //worm->xoff[worm->start] = 0;
+ //worm->yoff[worm->start] = 1;
+ worm->yhead--;
+ break;
+ case WORMDOWN:
+ //worm->xoff[worm->start] = 0;
+ //worm->yoff[worm->start] = -1;
+ worm->yhead++;
+ break;
+ case WORMLEFT:
+ //worm->xoff[worm->start] = 1;
+ //worm->yoff[worm->start] = 0;
+ worm->xhead--;
+ break;
+ case WORMRIGHT:
+ //worm->xoff[worm->start] = -1;
+ //worm->yoff[worm->start] = 0;
+ worm->xhead++;
+ break;
+ }
+
+ if (worm->xhead == BOARDWIDTH) {
+ worm->xhead = 0;
+ //worm->xoff[worm->start] += BOARDWIDTH;
+ }
+ if (worm->xhead < 0) {
+ worm->xhead = BOARDWIDTH - 1;
+ //worm->xoff[worm->start] -= BOARDWIDTH;
+ }
+ if (worm->yhead == BOARDHEIGHT) {
+ worm->yhead = 0;
+ //worm->yoff[worm->start] += BOARDHEIGHT;
+ }
+ if (worm->yhead < 0) {
+ worm->yhead = BOARDHEIGHT - 1;
+ //worm->yoff[worm->start] -= BOARDHEIGHT;
+ }
+
+ if ((level->walls[worm->xhead][worm->yhead] != EMPTYCHAR) &&
+ (level->walls[worm->xhead][worm->yhead] != WARPLETTER)) {
+ //gnibbles_cworm_grok_bonus (worm);
+ if ((level->walls[worm->xhead][worm->yhead] == BONUSREGULAR + 'A') &&
+ !gnibbles_boni_fake (boni, worm->xhead, worm->yhead)) {
+ gnibbles_boni_remove_bonus_final (boni, worm->xhead, worm->yhead);
+
+ if (boni->numleft != 0)
+ gnibbles_add_bonus (1);
+
+ } else
+ gnibbles_boni_remove_bonus_final (boni, worm->xhead, worm->yhead);
+ }
+
+ if (level->walls[worm->xhead][worm->yhead] == WARPLETTER) {
+ //gnibbles_warpmanager_worm_change_pos (warpmanager, worm);
+ //games_sound_play ("teleport");
+ }
+
+ worm->start++;
+
+ if (worm->start == CAPACITY)
+ worm->start = 0;
+
+ level->walls[worm->xhead][worm->yhead] = WORMCHAR + worm->number;
+/*
+ gnibbles_draw_pixmap (properties->wormprops[worm->number]->color,
+ worm->xhead, worm->yhead);
+
+ if (key_queue[worm->number] && !g_queue_is_empty (key_queue[worm->number])) {
+ gnibbles_cworm_dequeue_keypress (worm);
+ }
+*/
+}
+
+gint
+gnibbles_cworm_can_move_to (GnibblesCWorm * worm, gint x, gint y)
+{
+ if (worm->xhead == x)
+ return worm->yhead - 1 == y || worm->yhead + 1 == y;
+ if (worm->yhead == y)
+ return worm->xhead - 1 == x || worm->xhead + 1 == x;
+ return FALSE;
+}
+
+void
+gnibbles_cworm_position_move_head (GnibblesCWorm * worm, gint *x, gint *y)
+{
+ *x = worm->xhead;
+ *y = worm->yhead;
+
+ switch (worm->direction) {
+ case WORMUP:
+ *y = worm->yhead - 1;
+ break;
+ case WORMDOWN:
+ *y = worm->yhead + 1;
+ break;
+ case WORMLEFT:
+ *x = worm->xhead - 1;
+ break;
+ case WORMRIGHT:
+ *x = worm->xhead + 1;
+ break;
+ }
+
+ if (*x == BOARDWIDTH) {
+ *x = 0;
+ }
+ if (*x < 0) {
+ *x = BOARDWIDTH - 1;
+ }
+ if (*y == BOARDHEIGHT) {
+ *y = 0;
+ }
+ if (*y < 0) {
+ *y = BOARDHEIGHT - 1;
+ }
+}
+
+gint
+gnibbles_cworm_test_move_head (GnibblesCWorm * worm)
+{
+ int x, y;
+
+ gnibbles_cworm_position_move_head(worm, &x, &y);
+
+ if (level->walls[x][y] > EMPTYCHAR && level->walls[x][y] < 'z' + properties->numworms)
+ return (FALSE);
+
+ return TRUE;
+}
+
+gint
+gnibbles_cworm_is_move_safe (GnibblesCWorm * worm)
+{
+ int x, y, i;
+
+ gnibbles_cworm_position_move_head(worm, &x, &y);
+
+ for (i = 0; i < properties->numworms; i++) {
+ if (i != worm->number) {
+ if (gnibbles_cworm_can_move_to (cworms[i], x, y))
+ return (FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+void
+gnibbles_cworm_move_tail (GnibblesCWorm * worm)
+{
+ if (worm->change <= 0) {
+ //gnibbles_draw_pixmap (BLANKPIXMAP, worm->xtail, worm->ytail);
+ //worm->xtail -= worm->xoff[worm->stop];
+ //worm->ytail -= worm->yoff[worm->stop];
+ worm->stop++;
+ if (worm->stop == CAPACITY)
+ worm->stop = 0;
+ if (worm->change) {
+ //gnibbles_draw_pixmap (BLANKPIXMAP, worm->xtail, worm->ytail);
+ level->walls[worm->xtail][worm->ytail] = EMPTYCHAR;
+ //worm->xtail -= worm->xoff[worm->stop];
+ //worm->ytail -= worm->yoff[worm->stop];
+ worm->stop++;
+ if (worm->stop == CAPACITY)
+ worm->stop = 0;
+ worm->change++;
+ worm->length--;
+ }
+ } else {
+ worm->change--;
+ worm->length++;
+ }
+}
+
+/* Check whether the worm will be trapped in a dead end. A location
+ within the dead end and the length of the worm is given. This
+ prevents worms getting trapped in a spiral, or in a corner sharper
+ than 90 degrees. runnumber is a unique number used to update the
+ deadend board. The principle of the deadend board is that it marks
+ all squares previously checked, so the exact size of the deadend
+ can be calculated in O(n) time; to prevent the need to clear it
+ afterwards, a different number is stored in the board each time
+ (the number will not have been previously used, so the board will
+ appear empty). Although in theory deadend_runnumber may wrap round,
+ after 4 billion steps the entire board is likely to have been
+ overwritten anyway. */
+static guint deadendboard[BOARDWIDTH][BOARDHEIGHT] = {{0}};
+static guint deadend_runnumber = 0;
+static gint
+gnibbles_cworm_ai_deadend (gint x, gint y, gint lengthleft)
+{
+ gint cdir, cx, cy;
+
+ if (x >= BOARDWIDTH)
+ x = 0;
+ if (x < 0)
+ x = BOARDWIDTH - 1;
+ if (y >= BOARDHEIGHT)
+ y = 0;
+ if (y < 0)
+ y = BOARDHEIGHT - 1;
+
+ if (! lengthleft)
+ return 0;
+
+ cdir = 5;
+ while (--cdir) {
+ cx = x;
+ cy = y;
+ switch (cdir) {
+ case WORMUP:
+ cy -= 1;
+ break;
+ case WORMDOWN:
+ cy += 1;
+ break;
+ case WORMLEFT:
+ cx -= 1;
+ break;
+ case WORMRIGHT:
+ cx += 1;
+ break;
+ }
+
+ if (cx >= BOARDWIDTH)
+ cx = 0;
+ if (cx < 0)
+ cx = BOARDWIDTH - 1;
+ if (cy >= BOARDHEIGHT)
+ cy = 0;
+ if (cy < 0)
+ cy = BOARDHEIGHT - 1;
+
+ if ((level->walls[cx][cy] <= EMPTYCHAR
+ || level->walls[x][y] >= 'z' + properties->numworms)
+ && deadendboard[cx][cy] != deadend_runnumber) {
+ deadendboard[cx][cy] = deadend_runnumber;
+ lengthleft = gnibbles_cworm_ai_deadend(cx, cy, lengthleft - 1);
+ if (!lengthleft)
+ return 0;
+ }
+ }
+ return lengthleft;
+}
+
+/* Check a deadend starting from the next square in this direction,
+ rather than from this square. Also block off the squares near worm
+ heads, so that humans can't kill AI players by trapping them
+ against a wall. The given length is quartered and squared; this
+ allows for the situation where the worm has gone round in a square
+ and is about to get trapped in a spiral. However, it's set to at
+ least BOARDWIDTH, so that on the levels with long thin paths a worm
+ won't start down the path if it'll crash at the other end. */
+static gint
+gnibbles_cworm_ai_deadend_after (gint x, gint y, gint dir, gint length)
+{
+ gint cx, cy, cl, i;
+
+ if (x < 0 || x >= BOARDWIDTH || y < 0 || y >= BOARDHEIGHT) {
+ return 0;
+ }
+
+ ++deadend_runnumber;
+
+ if (dir > 4)
+ dir = 1;
+ if (dir < 1)
+ dir = 4;
+
+ i = properties->numworms;
+ while(i--) {
+ cx = cworms[i]->xhead;
+ cy = cworms[i]->yhead;
+ if(cx != x || cy != y) {
+ if(cx > 0) deadendboard[cx-1][cy] = deadend_runnumber;
+ if(cy > 0) deadendboard[cx][cy-1] = deadend_runnumber;
+ if(cx < BOARDWIDTH-1) deadendboard[cx+1][cy] = deadend_runnumber;
+ if(cy < BOARDHEIGHT-1) deadendboard[cx][cy+1] = deadend_runnumber;
+ }
+ }
+
+ cx = x;
+ cy = y;
+ switch (dir) {
+ case WORMUP:
+ cy -= 1;
+ break;
+ case WORMDOWN:
+ cy += 1;
+ break;
+ case WORMLEFT:
+ cx -= 1;
+ break;
+ case WORMRIGHT:
+ cx += 1;
+ break;
+ }
+
+ if (cx >= BOARDWIDTH)
+ cx = 0;
+ if (cx < 0)
+ cx = BOARDWIDTH - 1;
+ if (cy >= BOARDHEIGHT)
+ cy = 0;
+ if (cy < 0)
+ cy = BOARDHEIGHT - 1;
+
+ deadendboard[x][y] = deadend_runnumber;
+ deadendboard[cx][cy] = deadend_runnumber;
+
+ cl = (length * length) / 16;
+ if (cl < BOARDWIDTH)
+ cl = BOARDWIDTH;
+ return gnibbles_cworm_ai_deadend (cx, cy, cl);
+}
+
+/* Check to see if another worm's head is too close in front of us;
+ that is, that it's within 3 in the direction we're going and within
+ 1 to the side. */
+static gint
+gnibbles_cworm_ai_tooclose (GnibblesCWorm * worm)
+{
+ gint i = properties->numworms;
+ gint dx, dy;
+ while (i--) {
+ dx = worm->xhead - cworms[i]->xhead;
+ dy = worm->yhead - cworms[i]->yhead;
+ switch (worm->direction)
+ {
+ case WORMUP:
+ if (dy > 0 && dy <= 3 && dx >= -1 && dx <= 1)
+ return 1;
+ break;
+ case WORMDOWN:
+ if (dy < 0 && dy >= -3 && dx >= -1 && dx <= 1)
+ return 1;
+ break;
+ case WORMLEFT:
+ if (dx > 0 && dx <= 3 && dy >= -1 && dy <= 1)
+ return 1;
+ break;
+ case WORMRIGHT:
+ if (dx < 0 && dx >= -3 && dy >= -1 && dy <= 1)
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+static gint
+gnibbles_cworm_ai_wander (gint x, gint y, gint dir, gint ox, gint oy)
+{
+ if (dir > 4)
+ dir = 1;
+ if (dir < 1)
+ dir = 4;
+
+ switch (dir) {
+ case WORMUP:
+ y -= 1;
+ break;
+ case WORMDOWN:
+ y += 1;
+ break;
+ case WORMLEFT:
+ x -= 1;
+ break;
+ case WORMRIGHT:
+ x += 1;
+ break;
+ }
+
+ if (x >= BOARDWIDTH)
+ x = 0;
+ if (x < 0)
+ x = BOARDWIDTH - 1;
+ if (y >= BOARDHEIGHT)
+ y = 0;
+ if (y < 0)
+ y = BOARDHEIGHT - 1;
+
+ switch (level->walls[x][y] - 'A') {
+ case BONUSREGULAR:
+ case BONUSDOUBLE:
+ case BONUSLIFE:
+ case BONUSREVERSE:
+ return 1;
+ break;
+ case BONUSHALF:
+ return 0;
+ break;
+ default:
+ if (level->walls[x][y] > EMPTYCHAR && level->walls[x][y] < 'z' + properties->numworms) {
+ return 0;
+ } else {
+ if (ox == x && oy == y)
+ return 0;
+ return gnibbles_cworm_ai_wander (x, y, dir, ox, oy);
+ }
+ break;
+ }
+}
+
+/* Determines the direction of the AI worm. */
+void
+gnibbles_cworm_ai_move (GnibblesCWorm * worm)
+{
+ int opposite, dir, left, right, front;
+ gint bestyet, bestdir, thislen, olddir;
+
+ opposite = (worm->direction + 1) % 4 + 1;
+
+ front = gnibbles_cworm_ai_wander
+ (worm->xhead, worm->yhead, worm->direction, worm->xhead, worm->yhead);
+ left = gnibbles_cworm_ai_wander
+ (worm->xhead, worm->yhead, worm->direction - 1, worm->xhead, worm->yhead);
+ right = gnibbles_cworm_ai_wander
+ (worm->xhead, worm->yhead, worm->direction + 1, worm->xhead, worm->yhead);
+
+ if (!front) {
+ if (left) {
+ // Found a bonus to the left
+ dir = worm->direction - 1;
+ if (dir < 1)
+ dir = 4;
+ worm->direction = dir;
+ } else if (right) {
+ // Found a bonus to the right
+ dir = worm->direction + 1;
+ if (dir > 4)
+ dir = 1;
+ worm->direction = dir;
+ } else {
+ // Else move in random direction at random time intervals
+ if (rand () % 30 == 1) {
+ dir = worm->direction + (rand() % 2 ? 1 : -1);
+ if (dir != opposite) {
+ if (dir > 4)
+ dir = 1;
+ if (dir < 1)
+ dir = 4;
+ worm->direction = dir;
+ }
+ }
+ }
+ }
+
+ /* Avoid walls, dead-ends and other worm's heads. This is done using
+ an evalution function which is CAPACITY for a wall, 4 if another
+ worm's head is in the tooclose area, 4 if another worm's head
+ could move to the same location as ours, plus 0 if there's no
+ dead-end, or the amount that doesn't fit for a deadend. olddir's
+ score is reduced by 100, to favour it, but only if its score is 0
+ otherwise; this is so that if we're currently trapped in a dead
+ end, the worm will move in a space-filling manner in the hope
+ that the dead end will disappear (e.g. if it's made from the tail
+ of some worm, as often happens). */
+ olddir = worm->direction;
+ bestyet = CAPACITY*2;
+ bestdir = -1;
+ for (dir = 1; dir <= 4; dir++) {
+ worm->direction = dir;
+ if (dir == opposite) continue;
+ thislen = 0;
+ if(!gnibbles_cworm_test_move_head (worm))
+ thislen += CAPACITY;
+ if(gnibbles_cworm_ai_tooclose (worm))
+ thislen += 4;
+ if(!gnibbles_cworm_is_move_safe (worm))
+ thislen += 4;
+ thislen += gnibbles_cworm_ai_deadend_after
+ (worm->xhead, worm->yhead, dir, worm->length + worm->change);
+ if (dir == olddir && !thislen)
+ thislen -= 100;
+ /* If the favoured direction isn't appropriate, then choose
+ another direction at random rather than favouring one in
+ particular, to stop the worms bunching in the bottom-
+ right corner of the board. */
+ if (thislen <= 0)
+ thislen -= random() % 100;
+ if (thislen < bestyet)
+ {
+ bestyet = thislen;
+ bestdir = dir;
+ }
+ }
+ if (bestdir == -1) /* this should never happen, but just in case... */
+ bestdir = olddir;
+ worm->direction = bestdir;
+
+ /* Make sure we are at least avoiding walls.
+ * Mostly other snakes should avoid our head. */
+ for (dir = 1; dir <= 4; dir++) {
+ if (dir == opposite) continue;
+ if (!gnibbles_cworm_test_move_head (worm)) {
+ worm->direction = dir;
+ } else {
+ continue;
+ }
+ }
+}
diff --git a/gnibbles/worm-clutter.h b/gnibbles/worm-clutter.h
index 1cea665..3b887c9 100644
--- a/gnibbles/worm-clutter.h
+++ b/gnibbles/worm-clutter.h
@@ -43,6 +43,7 @@ typedef struct {
GList *list;
gint xstart, ystart;
guint xhead, yhead;
+ guint xtail, ytail;
gint direction;
gint direction_start;
gint length;
@@ -50,6 +51,9 @@ typedef struct {
guint score;
guint number;
gboolean inverse;
+ gint start;
+ gint stop;
+ gint change;
} GnibblesCWorm;
typedef struct {
@@ -72,9 +76,12 @@ gint gnibbles_cworm_lose_life (GnibblesCWorm * worm);
void gnibbles_cworm_resize (GnibblesCWorm *worm, gint newtile);
gint gnibbles_cworm_handle_keypress (GnibblesCWorm * worm, guint keyval);
+void gnibbles_cworm_draw_head (GnibblesCWorm * worm);
gint gnibbles_cworm_can_move_to (GnibblesCWorm * worm, gint x, gint y);
+void gnibbles_cworm_position_move_head (GnibblesCWorm * worm, gint *x, gint *y);
+gint gnibbles_cworm_test_move_head (GnibblesCWorm * worm);
gint gnibbles_cworm_is_move_safe (GnibblesCWorm * worm);
-void gnibbles_cworm_reset (GnibblesCWorm * worm);
+void gnibbles_cworm_move_tail (GnibblesCWorm * worm);
void gnibbles_cworm_ai_move (GnibblesCWorm * worm);
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]