ref: dd155881cf69b09d8e33ad2f4d6ef5a96eaa097c
dir: /engine.c/
#include "port.h" #include "util.h" #include "engine.h" #include "netclient.h" /* * Sets up a new level and fills it */ void initlevel(Game *game) { int i, cnt, x, y; for(x = 0; x < SzX; x++) for(y = 0; y < SzY; y++) game->ogrid[x][y] = Prev; switch(game->difficulty) { case DEasy: cnt = 10 + nrand(5); break; case DMed: cnt = 5 + nrand(5); break; case DHard: cnt = 1 + nrand(5); break; case DImp: default: cnt = 0; break; } game->ogrid[SzX/2][SzY/2] = Glenda; setmap: for(i = 0 ; i < cnt ; i++) { /* ensure walls dont overlap with other walls or glenda */ do { x = nrand(SzX); y = nrand(SzY); }while(game->ogrid[x][y] == Glenda || game->ogrid[x][y] == Wall); game->ogrid[x][y] = Wall; } /* ensure we don't make an unfair map */ if(checkstate(game) == Won && findmin(game, Pt(SzX/2, SzY/2)) > 100) goto setmap; memcpy(game->grid, game->ogrid, sizeof(game->grid)); game->state = Start; game->turn = 0; } /* * Returns position */ Point movedir(int dir, Point p) { int x = p.x; int y = p.y; switch(dir) { case NE: return Pt(x+(y%2?1:0), y-1); case E: return Pt(x+1, y); case SE: return Pt(x+(y%2?1:0), y+1); case SW: return Pt(x+(y%2?0:-1), y+1); case W: return Pt(x-1, y); case NW: return Pt(x+(y%2?0:-1), y-1); default: sysfatal("andrey messed up big time: %d", dir); } } /* * Reverse of movedir, tells the direction for a dst from src */ int pointdir(Point src, Point dst) { Point p; /* hacky */ for(int i = NE ; i <= NW ; i++) { p = movedir(i, src); if(p.x == dst.x && p.y == dst.y) return i; } return Err; } /* * Moves glenda into direction */ int domove(Game *game, int dir) { Point src, dst; src = findglenda(game); dst = movedir(dir, src); if(game->grid[dst.x][dst.y] == Wall) return Wall; if(game->networked) return netmove(dir); game->grid[dst.x][dst.y] = Glenda; game->grid[src.x][src.y] = Prev; game->turn++; /* we shouldn't guess, server should tell us */ if(game->ptype[1] == Human && !game->networked) checkstate(game); return Ok; } /* * Puts a wall inside a point */ int doput(Game *game, Point p) { /* clients are expected to do their own error checking */ if(p.x > SzX || p.x < 0 || p.y > SzY || p.y < 0) return Err; if(game->grid[p.x][p.y] == Wall) return Wall; if(game->grid[p.x][p.y] == Glenda) return Glenda; if(game->networked) return netput(p.x, p.y); /* take a copy for undo */ memcpy(game->pgrid, game->grid, sizeof(game->grid)); game->grid[p.x][p.y] = Wall; /* assumes defenders start game first */ if(game->state == Start) game->state = Playing; game->turn++; /* reset the board scores */ for(int x = 0; x < SzX; x++) for(int y = 0; y < SzY; y++) if(game->grid[x][y] != Wall && game->grid[x][y] != Glenda) game->grid[x][y] = 100; /* we need it to check game state, even if not playing with computer */ if(game->ptype[1] == Computer) nextglenda(game); else if(game->ptype[1] == Human && !game->networked) checkstate(game); return Ok; } /* * Returns glenda's position */ Point findglenda(Game *game) { for(int x = 0; x < SzX; x++) for(int y = 0; y < SzY; y++) if(game->grid[x][y] == 1000) return Pt(x, y); return Pt(-1, -1); } /* * the following two routines constitute the "game AI" * they score the field based on the number of moves * required to reach the edge from a particular point * scores > 100 are "dead spots" (this assumes the field * is not larger than ~100*2 * * routines need to run at least twice to ensure a field is properly * scored: there are errors that creep up due to the nature of * traversing the board */ int score1(Game *game, Point p) { int min = 999; if(p.x == 0 || p.x == SzX-1 || p.y == 0 || p.y == SzY-1) return 1; /* we can always escape from the edges */ if(findmin(game, p) < min) min = findmin(game, p); if(min >= 998) return 998; return 1+min; } /* * calculates scores of whole graph * O(n³), but why? */ void calc(Game *game) { /* assumes SzX = SzY */ for(int i = 0; i < SzX; i++) for(int x = i; x < SzX-i; x++) for(int y = i; y < SzY-i; y++) if(game->grid[x][y] != Wall && game->grid[x][y] != Glenda) game->grid[x][y] = score1(game, Pt(x, y)); } /* * finds the node with least score around the p */ int findmin(Game *game, Point p) { Point tmp; int next, min = 998; for(int dir = NE; dir <= NW; dir++) { tmp = movedir(dir, p); next = game->grid[tmp.x][tmp.y]; if(next < min) min = next; } return min; } /* * Makes a move for glenda * only used in single player */ void nextglenda(Game *game) { int min = 999, next, dir, nextdir = 0, count = 0; Point tmp, p = findglenda(game); if(game->networked) return; calc(game); calc(game); calc(game); for(dir = NE; dir <= NW; dir++) { tmp = movedir(dir, p); next = game->grid[tmp.x][tmp.y]; if(next < min) { min = next; nextdir = dir; ++count; } else if(next == min) nextdir = (nrand(++count) == 0) ? dir : nextdir; } if(min > 100) game->state = Won; else domove(game, nextdir); p = findglenda(game); if(p.x == 0 || p.x == SzX-1 || p.y == 0 || p.y == SzY-1) game->state = Lost; } /* * See if game is over */ int checkstate(Game *game) { Point p = findglenda(game); calc(game); calc(game); calc(game); if(findmin(game, p) > 100) game->state = Won; if(p.x == 0 || p.x == SzX-1 || p.y == 0 || p.y == SzY-1) game->state = Lost; return game->state; } /* * Resets the game position */ void restart(Game *game) { game->turn = 0; memcpy(game->grid, game->ogrid, sizeof(game->grid)); game->state = Start; } /* * Undos last move */ void undo(Game *game) { int g[SzX][SzY]; if(game->state == Start || game->turn < 2) return; /* revert turn counter, can be abused by one side */ game->turn -= 2; /* swap grids */ memcpy(g, game->grid, sizeof g); memcpy(game->grid, game->pgrid, sizeof g); memcpy(game->pgrid, g, sizeof g); } int isplaying(Game *game) { return game->state == Playing || game->state == Start; }