Andy's LEGO® Mindstorms® Page
About this site
  
What's New?
  
Standard Disclaimer
Quite C
  
Francesco Ferrara
  
Getting Started
  
Installing Quite C
  
Compiling a project
  
Sample Code
Chess
  
The Robot Chess Project
  
Programming Chess
  
First Attempt
  
Version 0
  
Version 1
  
The Program
  
The State of Play
  
The User Interface
  
The Move Generator
  
The Move Applier
  
The Rating Algorithm
  
The Search Algorithm
  
Putting it all together
  
The Robot
  
The Head
  
Moving the head
  
Movies
  
Downloads: Source Executables, MLCad Models
4-in-a-Robot
  
4-in-a-Robot - the Robot
  
The 4-in-a-Robot Base
  
The 4-in-a-Robot Delivery Mechanism
  
The 4-in-a-Robot Controller
  
4-in-a-Robot - the Code
  
The 4-in-a-Robot Algorithm
  
The 4-in-a-Robot Control Programming
  
The 4-in-a-Robot Robot-less version
  
Installing 4-in-a-robot
  
Playing the robotless 4-in-a-robot
  
View the source code
Speech
  
The problems with speech
  
Sounds familiar
  
H8 Timers Background
  
Trial and Error
  
Volume Zapper
  
Actually Speaking
  
Speak.c - the code
  
The VB code generator
Some ideas for the future
  
Room positioning robot
  
Neural Net Bot
  
Pianola
  
Text to speech
LEGO® Mindstorms® Links

[268744]

View the source code

  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;
}



Back to: Playing the robotless 4-in-a-robot

Show Topic: The 4-in-a-Robot Robot-less version