35
2D1350 Programmeringsparadigm Pointers Arrays Structures Artificial intelligence and game playing Lab assignment

2D1350 Programmeringsparadigm Pointers Arrays Structures Artificial intelligence and game playing Lab assignment

  • View
    221

  • Download
    1

Embed Size (px)

Citation preview

2D1350 Programmeringsparadigm Pointers Arrays Structures Artificial intelligence and game playing Lab assignment

Games as Search Problems The behavior / actions of the opponent are

unpredictable, therefore search for a “worst-case”-plan.

Time limit, therefore complete search is not feasible and an approximation is needed

Algorithm for perfect play (van Neumann 1944)

Finite horizon, approximate evaluation (Zuse 1945, Shannon 1950, Samuel 1952)

Pruning search tree (McCarthy 1956)

Types of Game

deterministic Stochastic

Perfect information Chess, checkers, connect-4, go, othello

Backgammon, monopoly

Imperfect information

Bridge, poker, scrabble

Min-Max-Search Optimal strategy for deterministic, perfect-

information game Idea: Choose move that results in position with

highest min-max-value = best achievable payoff against best opponents play

5

3 2

3 12 8

5

5 7 9 4 2 7

Max:

Min:

A11

A2

A3

A13A12

A21 A23A22

A31 A33A32

A1

Min-Max-SearchFunction MINMAX-DECISION(game state) returns a move

for each move in PossibleMoves(game state) do

value[move] <- MINIMAX-VALUE(apply(move, game state))

end

return the move with the highest value[move]

Function MINMAX-VALUE(game state) returns a utility value

if TERMINAL-TEST(game state) then

return UTILITY(game state)

else if MAX is to move in game state

return the highest MINMAX-VALUE of SUCCESSORS(game state)

else

return the lowest MINMAX-VALUE of SUCCESSORS(game state)

Min-Max Properties Complete: yes, if search tree is finite Optimal : yes, if opponent plays optimal Time complexity : O(bm) Space complexity : O(bm) depth first search Chess b~35 possible moves in each state,

m~100 moves per game -> exact solution infeasible

Standard solution cutoff test for search (e.g. depth limit) evaluation function : approximates utility of

board position

Evaluation Scheme For chess for example typically linear weighted

sum of features Utility(s) = w1 f1(s) + w2 f2(s) + …wn fn(s)

w1=9

f1(s)= #white queens - #black queens

w2=5

f2(s) = #white rooks - #black rooks

etc.

Cutting of Search Min-Max-Search with Cut-Off requires

1. CUTOFF criterion, usually based on search depth

2. UTILITY function needs an evaluation scheme for non-terminal game states

Ply = one half-move (move by one player) Chess:

4-ply = novice 8-ply = PC, human master 12-ply = Deep Blue, Kasparov

Min-Max-Search with Cut-Off

Function MINMAX-DECISION(game) returns a move

for each move in PossibleMoves(game state) do

value[move] <- MINIMAX-VALUE(apply(move, game state))

end

return the move with the highest value[move]

Function MINMAX-VALUE(game state, depth) returns a utility value

if CUTOFF-TEST(depth) or TERMINAL-TEST(game state)

return UTILITY(game state)

else if MAX is to move in game state

return the highest MINMAX-VALUE(SUCCESSOR(game state),depth+1)

else

return the lowest MINMAX-VALUE(SUCCESSOR(game state), depth+1)

Connect-4

two player game 7x6 rectangular board placed vertically 21 red, 21 yellow tokens players alternate by dropping a token into one of the

seven columns, the token falls down to the lowest unoccupied square

a player wins if she connects four token vertically, horizontally or diagonally

if the board is filled (42 tokens played) and no player has aligned four tokens the game ends in a draw

Connect-4

win for yellow win for red

draw

Connect-4 Utility Function Odd square: is a square belonging to an odd row (1,3,5) Even square: is a square belonging to an even row (2,4,6) Threats: A threat is a group of three tokens of the same

color which has the fourth square empty and also the square below the empty square is empty

Odd threat: is a threat in which the empty square is odd Even threat: is a threat in which the empty square is even If red (moves first) has an odd threat and black cannot

connect four tokens anywhere else red will win. If black (moves second) has an even threat and black

cannot connect four tokens anywhere else black will win. At the beginning of the game it is advantageous to place

tokens in the central columns.

Lab Assignment Code for connect-4 available in game.h and

game.c Copy files game.h, game.c, Makefile into your

local directory To add gcc to your list of modules type

module add gcc/3.1or add this line to .modules

To compile game.c into executable type make

Lab Assignment Design and implement a game playing program for the

deterministic two player game Connect-4 Features

The program should be able to play against itself or a human opponent.

The program should visualize the evolution of the game and print out its own estimate of the utility of the current game state.

The program should determine who won or if the game ended in a draw

Implement the min-max-search algorithm Implement a proper utility function for Connect-4 Test the program by letting it play against itself and against a

human opponent

Connect-4 Game State#define ROWS 6 /* number of rows in connect-4 */

#define COLS 7 /* number of columns in connect-4 */

#define RED -1 /* value for red tokens and red player */

#define BLACK 1 /* value for black tokens and black player */

struct Game

{

int board[ROWS][COLS]; /* -1 token red player, 1 token black player,

0 empty square */

int currentplayer; /* -1 red player, 1 black player */

int tokensonboard; /* counts the number of tokens on the board */

};

Connect-4 Move

struct Move

{

int row; /* row coordinate of square */

int col; /* column coordinate of square */

int token; /* -1 red token, 1 black token */

};

void InitGame()void InitGame(struct Game *game) /* pointer reference to game state */

{

int i;

int j;

for (i=0; i < ROWS; i++)

for (j=0; j < COLS; j++)

(*game).board[i][j]=0; /* empty board */

(*game).currentplayer=RED; /* red player to start game */

(*game).tokensonboard=0;

};

void MakeMove()void MakeMove(struct Game *game, struct Move move)

{

#if DEBUG

assert((*game).board[move.row][move.col]==0); /* assert square is empty */

if (move.row>0)

assert((*game).board[move.row-1][move.col]!=0); /* assert square below is occupied */

assert((*game).currentplayer==move.token); /* assert correct player moves */

#endif

(*game).board[move.row][move.col]=move.token; /* place token at square */

(*game).currentplayer*=-1; /* switch player */

(*game).tokensonboard++; /* increment number of tokens on board */

}

void UndoMove()void UndoMove(struct Game *game, struct Move move){#if DEBUG assert((*game).board[move.row][move.col]!=0); /* assert square is occupied */ if (move.row<ROWS-1) assert((*game).board[move.row+1][move.col]!=0); /* assert square above is

empty */ assert((*game).currentplayer!=move.token); /* assert correct player moves */#endif (*game).board[move.row][move.col]=0; /* remove token from square */ (*game).currentplayer*=-1; /* switch player */ (*game).tokensonboard--; /* decrement number of tokens on board */}

int Win()int Win(struct Game *game, int player){ int i; int j; for (j=0;j<COLS;j++) for(i=0;i<ROWS-3;i++) /* check for group of four vertical tokens */ { int count=0; /* counts number of consecutive tokens */

while((count < 4) && ((*game).board[i+count][j]==player)) /* check if token is owned by player and not 4 tokens yet */

count++; if (count==4) /* four tokens in column */

return 1; /* win for player*/ } … /* check for horizontal and diagonal groups of four */

int Win() for (j=0;j<COLS-3;j++) for(i=0;i<ROWS;i++) /* check for group of four horizontal four tokens */ { int count=0; /* counts number of consecutive tokens */

while((count < 4) && ((*game).board[i][j+count]==player)) /* check if token is owned by player and not 4 tokens yet */

count++; if (count==4) /* four tokens in a row */

return 1; /* win for player */ }

int Win()

for (j=0;j<COLS-3;j++) for(i=0;i<ROWS-3;i++) /* check for four tokens in an upward diagonal */ { int count=0; /* counts number of consecutive tokens */

while((count < 4) && ((*game).board[i+count][j+count]==player)) /* check if token is owned by player and not 4 tokens yet */

count++; if (count==4) /* four tokens in a diagonal */

return 1; /* win for player */ }

int Win()

for (j=0;j<COLS-3;j++)

for(i=3;i<ROWS;i++) /* check for four tokens in a downward diagonal */

{

int count=0; /* counts number of consecutive tokens */

while((count < 4) && ((*game).board[i-count][j+count]==player))

/* check if token is owned by player and not 4 tokens yet */

count++;

if (count==4) /* four tokens in a diagonal */

return 1; /* win for player */

}

return 0; /* no win for player */

}

int Draw()int Draw(struct Game *game)

{

if ((*game).tokensonboard<42)

return 0;

else return (!Win(game,RED) && !Win(game,BLACK));

}

int Row()

int Row(struct Game *game, int col)

/* computes the row on which token ends when dropped in column col */

{

int row=0;

while((row<ROWS) && (*game).board[row][col]!=0)

row++;

return row;

}

void PossibleMoves()void PossibleMoves(struct Game *game, int *number_of_moves, struct Move moves[]) /* computes the possible moves , number_of_moves returns the number of available moves, moves[]

contains array of moves */ { int i; *number_of_moves=0; for (i=0;i<COLS;i++) { int row=Row(game,i); /* computes first empty square in col i */ if (row<ROWS) /* column has an empty square */ { moves[*number_of_moves].row=row; moves[*number_of_moves].col=i; moves[*number_of_moves].token=(*game).currentplayer; (*number_of_moves)++; } }}

int Utility()int Utility(struct Game *game){ if (Draw(game)) return 0; if (Win(game,RED)) return 1000; /* maximum utility for winning */ if (Win(game,BLACK)) return -1000; /* minimum utility for loosing */ /* non-terminal game state */ /* HERE GOES YOUR CODE TO COMPUTE UTILITY OF NON-

TERMINAL BOARD STATES */

return 0;}

Input / Output Functionsvoid DisplayBoard(struct Game *game); /* print board state on screen */

void DisplayMove(struct Move move); /* print move on screen */

void EnterMove(struct Move *move, struct Game *game);

/* reads in a move from the keyboard */

int main()

int main(int argc, char *argv[])

{

int i;

struct Game game; /* variable to store the game state */

struct Move moves[COLS]; /* array to store possible moves */

int number_of_moves;

int playagainsthuman=0;

/* computer plays against itself (0) or against human (1) */

int main() for (i=1; i<argc; i++) /* iterate through all command line arguments */ { if(strcmp(argv[i],"-p")==0) /* if command line argument -p human opponent */ {

playagainsthuman=1; printf("Human player plays black\n");

} if(strcmp(argv[i],"-h")==0) /* if command line argument -h print help */ {

printf("game [-p] [-h]\n-p for play against human player\n-h for help\n"); return 0; /* quit program */ } }

int main()InitGame(&game); /* set up board */

while( !Draw(&game) && !Win(&game,RED) && !Win(&game,BLACK))

/* no draw or win */

{

int rand;

struct Move move;

DisplayBoard(&game); /* display board state */

PossibleMoves(&game,&number_of_moves,moves);

/* calculate available moves */

rand = (int) (drand48()*number_of_moves); /* pick a random move */

MakeMove(&game,moves[rand]); /* make move */

DisplayMove(moves[rand]); /* display move */

int main()if (playagainsthuman) /* play against human opponent */

{

DisplayBoard(&game); /* show board state after computer moved */

if (!Draw(&game) && !Win(&game,RED))

/* no draw and no computer win */

{

EnterMove(&move,&game); /* human player enters her move */

MakeMove(&game,move); /* make move */

}

} /* end of human player play */

} /* end of while not draw or win */

int main() DisplayBoard(&game); /* display board state */

if (Draw(&game))

printf("the game ended in a draw\n");

if (Win(&game, RED))

printf("player red won the game\n");

if (Win(&game, BLACK))

printf("player black won the game\n");

return 0;

} /* end of main */

MakefileOBJS = game.o

# for gcc compiler ....

CC = gcc

CFLAGS = -g -Wall

#naming executable

EXEC = game

all: $(EXEC)

%.o:%.c

$(CC) $(CFLAGS) -c $<

$(EXEC): $(OBJS)

$(CC) -o $@ $(OBJS)

Question of the Day

Draw a closed path of four straight lines that connects all nine dots.