; C4 ; Copyright (c) 2002 Thomas Mathys ; killer@vantage.ch ; ; This file is part of C4. ; ; C4 is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; C4 is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with C4; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %ifndef _AI_INC %define _AI_INC INFTY equ 1000000000 section .data ; table used to perform some primitive move "ordering": ; middle columns which are usually more important are ; searched first. moveorder dd 1,7,2,6,3,5,4 ; table used for static evaluation. ; this table is taken from 4st attack: it is much better ; than the table I used before =) evaltable: dd 0, 0, 0, 0, 0, 0, 0, 0, 0 dd 0, 3, 4, 5, 7, 5, 4, 3, 0 dd 0, 4, 6, 8,10, 8, 6, 4, 0 dd 0, 5, 8,12,13,12, 8, 5, 0 dd 0, 5, 8,12,13,12, 8, 5, 0 dd 0, 4, 6, 8,10, 8, 6, 4, 0 dd 0, 3, 4, 5, 7, 5, 4, 3, 0 dd 0, 0, 0, 0, 0, 0, 0, 0, 0 section .bss cpulevel resd 1 ; level of current cpu player bestval resd 1 ; value of best move found so far nbestmoves resd 1 ; # of best moves found so far bestmoves resd 7 ; array to hold all best moves section .text ;********************************************************** ; aiGetMove ; returns a move for a computer player ; ; input : eax = cpu level ( >= 1) ; output : eax = move ; destroys : everything ;********************************************************** aiGetMove: ; initialize variables mov [cpulevel],eax mov dword [bestval],-INFTY mov dword [nbestmoves],0 ; try every move mov ecx,6 .evalmoves: ; get move to make from move order table mov eax,[moveorder+ecx*4] ; if this is an invalid move, continue with next move BOARDISVALIDMOVE eax jz .nextmove ; make move for current player mov ebx,[currentplayer] call boardMakeMove ; evaluate move push eax ; save move # push ecx ; save loop counter push dword [cpulevel] ; ply mov ebx,[currentplayer] ; player BOARDGETOTHERPLAYER ebx push ebx push dword -INFTY ; alpha push dword INFTY ; beta call alphabeta neg eax ; How could I forget this ??? mov ebx,eax ; save result for later pop ecx ; restore loop counter pop eax ; restore move # ; undo move (eax = move #) call boardUndoMove ; let's see wether we have a new best move cmp ebx,[bestval] ; new best value found ? jle .nonewbestval mov [bestval],ebx ; yes -> save it mov dword [nbestmoves],1 ; delete everything that was in the list mov [bestmoves+0],eax ; save move number in list jmp short .nextmove ; continue with next move .nonewbestval: cmp ebx,[bestval] ; another best value found ? jne .nextmove mov ebx,[nbestmoves] ; yes -> add move to list mov [bestmoves+ebx*4],eax inc dword [nbestmoves] .nextmove: dec ecx js .done jmp .evalmoves .done: ; randomly pick one of the best moves call rand ; rand() % nbestmoves xor edx,edx div dword [nbestmoves] mov eax,[bestmoves+edx*4] ; get move from list ret ; test code : pick first move from list mov eax,[bestmoves+0] ret ;********************************************************** ; alphabeta ; ; input : see below ; output : eax = move value ; destroys : everything ;********************************************************** alphabeta: %define ply (ebp+20) %define player (ebp+16) %define alpha (ebp+12) %define beta (ebp+ 8) enter 0,0 ; win for other player -> end search mov eax,[player] BOARDGETOTHERPLAYER eax call boardIsWin or eax,eax jz .nowin mov eax,-1000000 mov ebx,[ply] shl ebx,10 sub eax,ebx leave ret 4*4 .nowin: ; board full but no win -> draw -> end search BOARDISFULL jnz .notfull xor eax,eax leave ret 4*4 .notfull: ; max search depth reached -> do static evaluation cmp dword [ply],0 je .staticeval ; for each move mov ecx,6 .evalmoves: ; while (alpha < beta) mov eax,[alpha] cmp eax,[beta] jge .done ; pick move from move order table mov eax,[moveorder+ecx*4] ; invalid move ? if so, continue with next move BOARDISVALIDMOVE eax jz .nextmove ; make move for current player mov ebx,[player] call boardMakeMove ; evaluate move push eax push ecx mov eax,[ply] ; ply = ply-1 dec eax push eax mov ebx,[player] ; player = other player BOARDGETOTHERPLAYER ebx push ebx mov ecx,[beta] ; alpha = -beta neg ecx push ecx mov edx,[alpha] ; beta = -alpha neg edx push edx call alphabeta neg eax ; new alpha ? cmp eax,[alpha] jle .nonewalpha mov [alpha],eax ; yes -> save it .nonewalpha: pop ecx pop eax ; undo move call boardUndoMove .nextmove: ; evaluate next move dec ecx jns .evalmoves .done: mov eax,[alpha] leave ret 4*4 ; static evaluation .staticeval: xor eax,eax mov esi,BWIDTH*BHEIGHT-1 .l: mov ebx,[board+esi*4] ; get stone from board cmp ebx,[player] ; player's stone ? jne .notplayer ; nope -> go on add eax,[evaltable+esi*4] ; yes -> add stone value to eax jmp .next ; next stone .notplayer: cmp ebx,EMPTY ; other player's stone ? je .empty ; nope -> go on sub eax,[evaltable+esi*4] ; yes -> sub stone value from eax .empty: .next: ; next stone dec esi jns .l leave ; eax contains static value ret 4*4 %undef ply %undef player %undef alpha %undef beta %endif