|
|
Should I post the source code... let me see how big it is first...
Oh, pretty tiny, so here goes 
#include <qc.h>
//4 in a row for Lego RCX //Many thanks to Francesco Ferrara without whom this wouldn't be possible! //Check out http://web.tiscalinet.it/ferrarafrancesco/qc/ for his wonderful QC!
//This is the robot-less release of 4inaRow //for the robot-based version, or any other questions or comments //feel free to contact me at lego@andyc.easynet.co.uk
//This source is completely without liscence or coyright: //you are welcome, neigh, encouraged to play with it, modify it, //copy it, print it out on a T-Shirt, or anything you like. //You could even try building your own Mindstorms four-in-a-robot! //One thing I do ask is that you rename this file if you do change it and distribute it //so that 4inaRow.c and 4inaRow.srec are always my version of this code
//Have fun!
//some useful constants #define EMPTY 0 #define PLAYER 1 #define CPU 2 #define TRUE -1 #define FALSE 0
//typedef structure for returning score and column typedef struct { int col; long score; } COLSCORE; //the board grid int board[6][7];
//the four direction vectors (downleft,left,upleft,up) int dx[]={-1,-1,-1,0}; int dy[]={-1,0,1,1};
//lookahead level int lookahead;
//useful routine to output long to display //ignores initial digits if longer than 5 digits void LCDLong(long i) { char out[6]; long v; strcpy(out," \0"); out[0]=' '; if (i<0) { out[0]='-'; i=-i; } if (i>10000L) { v=i/10000L; v%=10; out[1]='0'+v; v=i/10000L; i-=v*10000L; } if (i>1000L) { v=i/1000L; out[2]='0'+v; i-=v*1000L; } if (i>100L) { v=i/100L; out[3]='0'+v; i-=v*100L; } if (i>10L) { v=i/10L; out[4]='0'+v; i-=v*10L; } out[5]='0'+i; LCDPrints(out);
}
//useful routine to output integer to display void LCDInt(int i) { char out[6]; int v; strcpy(out," \0"); out[0]=' '; if (i<0) { out[0]='-'; i=-i; } if (i>10000) { v=i/10000; out[1]='0'+v; i-=v*10000; } if (i>1000) { v=i/1000; out[2]='0'+v; i-=v*1000; } if (i>100) { v=i/100; out[3]='0'+v; i-=v*100; } if (i>10) { v=i/10; out[4]='0'+v; i-=v*10; } out[5]='0'+i; LCDPrints(out); }
//clear the board void ClearBoard() { int x,y; for (x=0;x<7;x++) for (y=0;y<6;y++) board[y][x]=EMPTY; } //Plays (go) in column (x) void Play(int x, int go) { int y=0; while (board[y][x]>EMPTY) { y++; } board[y][x]=go; }
//Removes last go from column (x) void UnPlay(int x) { int y=0; while (board[y][x]>EMPTY) { y++; } y--; board[y][x]=EMPTY; }
//See if (p) has won int CheckWin(int p) { int d,x,y,cx,cy,c,t; for (x=0;x<7;x++) for (y=0;y<6;y++) for (d=0;d<4;d++) { //start from cx cy cx=x; cy=y; t=0; for (c=0;c<4;c++) { if (board[cy][cx]==p) t++; cx+=dx[d]; cy+=dy[d]; if ((cx<0)|(cx>6)|(cy<0)|(cy>5)) break; } if (t==4) return TRUE; } return FALSE; } //Check for draw (all of board is full) int CheckDraw() { int x,t; t=0; for (x=0;x<7;x++) if (board[5][x]>EMPTY) t++; if (t==7) return TRUE; else return FALSE; }
//return the basic heurustic score if player (go) plays the column (col)
long Score(int col, int go) { int sx,sy; //start positions int try, test, testc; //4 tries of 4 tests - testc=how many valid positions int cx,cy; //current try position int tx,ty; //current test position int dir; long score; //score long incr; //score increment int empty, current, opponent; //number of cells filled sx=col; //return minimum if column full if (board[5][col]>EMPTY) { return -30000L; }
//position to first free cell for (sy=0;sy<5;sy++) if (board[sy][col]==EMPTY) break; //now check all possible rows through (sx,sy) score=0; //four directions for (dir=0;dir<4;dir++) { cx=sx; cy=sy; //four possible rows through cell in current direction for (try=0;try<4;try++) { tx=cx; ty=cy; empty=0; current=0; opponent=0; testc=0; //check four positions for (test=0;test<4;test++) { if (board[ty][tx]==EMPTY) empty++; else if (board[ty][tx]==go) current++; else opponent++; tx+=dx[dir]; ty+=dy[dir]; testc++; if ((tx<0) | (tx>6) | (ty<0) | (ty>5)) break; } //did we get a score? if (testc==4) { //get score increment incr=0L; if ((current==3) & (empty==1)) //makes four in a row! incr=100000L; else if ((opponent==3) & (empty==1)) //blocks four in a row incr=10000L; else if ((current==2) & (empty==2)) //makes three in a row incr=1000L; else if ((opponent==2) & (empty==2)) //blocks three in a row incr=100L; else if ((current==1) & (empty==3)) //makes two in a row incr=10L; else if ((opponent==1) & (empty==3)) //blocks two in a row incr=2L; else if ((current==4)) //empty row incr=1L; score+=incr; } //next try, move back one in current direction cy-=dy[dir]; cx-=dx[dir]; if ((cx<0) | (cx>6) | (cy<0) | (cy>5)) break; } }
//return the score return score; }
//return column and score for the best move available, look ahead depth turns COLSCORE BestMove(int go, int depth) { long score, best, bestscore; int col; int opponent; COLSCORE cs; bestscore=-10000L; best=-1L; for (col=0;col<7;col++) { if (board[5][col]==EMPTY) { score=Score(col,go); //if we have a lookahead depth, subtract the score for the opponent /5 if (depth>0) { //pretend to play the move Play(col, go); if (go==CPU) opponent=PLAYER; else opponent=CPU; //subtract 1/5th of the score for the opponent cs=BestMove(opponent, depth-1); score-=cs.score/5L; //remove the pretend move UnPlay(col); } //is this the best score so far? if (score>bestscore) { bestscore=score; best=col; } } } cs.score=bestscore; cs.col=best; return cs; }
void ComputerGo() { char out[6]; COLSCORE cs; LCDPrints(" MY GO"); cs=BestMove(CPU, lookahead); Play(cs.col,CPU); //display go out[0]=' '; out[1]='C'; out[2]='P'; out[3]='U'; out[4]=' '; out[5]='0'+cs.col; LCDPrints(out); //wait for view button while (getc()!=VIEW); } void PlayerGo() { char c; char out[6]; int col; int ok; col=0; c=0; ok=FALSE; //get player input while (!ok) { while (c!=RUN) { out[0]=' '; out[1]='Y'; out[2]='O'; out[3]='U'; out[4]=' '; out[5]='0'+col; LCDPrints(out); c=getc(); if (c==VIEW) { col++; if (col==7) col=0; } } if (board[5][col]==EMPTY) ok=TRUE; else c=0; } Play(col,PLAYER); }
//Select lookahead : 1-EASY, 2-OK, 3-HARD, 4-SUPER void SetLookahead() { char c; lookahead=1; c=0; LCDPrints(" LEVEL"); while (c!=RUN) { c=getc(); if (c==VIEW) { lookahead++; if (lookahead==5) lookahead=1; switch (lookahead) { case 1: LCDPrints(" EASY"); break; case 2: LCDPrints(" OK"); break; case 3: LCDPrints(" HARD"); break; case 4: LCDPrints(" SUPER"); break; } } } }
//See if user wants to go first //displays CPU/YOU int GoFirst() { int first; char c; LCDPrints(" FIRST"); c=0; first=FALSE; while (c!=RUN) { c=getc(); if (c==VIEW) { if (first) { LCDPrints(" CPU"); first=FALSE; } else { LCDPrints(" YOU"); first=TRUE; } } } return first;
} int PlayAgain() { int again; char c; LCDPrints(" AGAIN"); c=0; again=TRUE; while (c!=RUN) { c=getc(); if (c==VIEW) { if (again) { LCDPrints(" NO"); again=FALSE; } else { LCDPrints(" YES"); again=TRUE; } } } return again; }
int main() { int playing; playing=TRUE; //keep running until user doesn't want to play again while (playing) { SetLookahead(); ClearBoard(); //see if user wants to go first if (GoFirst()) { //give the player a go PlayerGo(); //don't need to check for wins, as this is the first go! } //now keep going until someone wins while (playing) { //CPU goes next ComputerGo(); if (CheckWin(CPU)) { playing=FALSE; LCDPrints(" I WIN"); break; } PlayerGo(); if (CheckWin(PLAYER)) { playing=FALSE; LCDPrints(" U WON"); break; } if (CheckDraw()) { playing=FALSE; LCDPrints(" DRAW"); break; } } //wait for any key getc(); //play again? playing=PlayAgain(); } //finish, this will restore the RCX default rom return 0; }
|
|