You are on page 1of 47

Jon Lau Nielsen

jlni@itu.dk

Reversi in Java
September 7, 2009

Contents
1 Foreword
1.1 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 Assignment 1: BasicBoard
2.1 counter () . . . . . . . . .
2.2 score () . . . . . . . . . .
2.3 printState () . . . . . . . .
2.4 play () . . . . . . . . . . .
2.4.1 isValidMove () . .
2.4.2 flip () . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

4
4
4
6
6
7
8
9
10

3 Assignment 2: Board
11
3.1 validMoves () . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Assignment 3: Player
4.1 The Player . . . . .
4.1.1 init() . . . . .
4.1.2 name() . . .
4.1.3 move() . . . .
4.1.4 notify() . . .
4.2 The GUI . . . . . . .
4.2.1 buildGUI . .
4.2.2 mouseClicked
4.3 Human vs. Human .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

12
12
12
12
12
13
14
14
16
16

5 Assignment 4: ComputerPlayer
5.1 Structure . . . . . . . . . . . .
5.1.1 Minimax . . . . . . . .
5.1.2 Alpha-beta pruning . .
5.2 move() . . . . . . . . . . . . . .
5.2.1 myTurn() . . . . . . . .
5.2.2 hisTurn() . . . . . . . .
5.3 boardValue() . . . . . . . . . .
5.4 Human vs. Bot . . . . . . . . .
5.5 Bot vs. Bot . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

16
17
17
18
20
20
20
21
23
23

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

A JlniBasicBoard.java

25

B JlniBoard.java

31

C JlniPlayer.java

32

D JlniReversiBoard.java

35

E JlniSquare.java

39

F JlniComputerPlayer

41

Foreword

Please take notice before reading, that some comments has been stripped in
some listings/appendixes. Some code has also been slightly modified, to fit
inside the paper - e.g. a long if-statement with multible ands and ors but of course the code would still be able to compile into the game. For a
non-modified code, I would prefer if the reader took time, to look at the real
code.
Also please notice some helper methods like intPlayer and isGameOver
has not been showed and explained. This is simply because they are very simple,
and isnt very important for the design of the game.
The methods signatures, in the headlines, has been reduced - they wont be
showing public, return datatype and parameters.
Again, for full code, please see the appendix.
Ive created 3 small referee-classes, called HumanVsBot, HumanVsHuman
and BotVsBot, which allows you, the reader, to see the game in execution.

1.1

Structure

Before explaining the assignments, Ill briefly run through the structure of my
Reversi game. The project folder will contain 2 packages, Interfaces and jlni.
The classes inside the Interfaces-package, are designed to force my classes (inside
jlni-package) to create the methods it declares. The Interfaces-package, and all
of its content, has been developed by Alexandre Buisse.

Assignment 1: BasicBoard

Assignment 1 consists of the JlniBasicBoard-class. The goal for the class, is to


enable 2 human players, to take turns and play a full game of Reversi together.
For full code, please see appendix A.
The class will implement the BasicBoard, which are imported from the
Interface-package, see below in listening 1:
Listing 1: Importing and implementing
1
2
3
4

// I m p o r t i n g I n t e r f a c e s .
import I n t e r f a c e s . ;
// Implementing
public c l a s s J l n i B a s i c B o a r d implements BasicBoard

The design of the JlniBasicBoard-class with the implemented methods, is


seen in figure 1. The class is being expanded, with helper-methods, throughout
the assignment - so figure 1 is only a representation of the implementation.

Figure 1: BasicBoards design


Before I present the methods, Ill present the public declared instance variables, which will be used throughout the methods.
Below, in listing 2, the first variables are declared.
Listing 2: Boolean Players
1
2
3
4

public
public
public
public

boolean gameOver = f a l s e ;
int totalTurns = 0 ;
i n t a r r S t a t e [ ] [ ] = new i n t [ 8 ] [ 8 ] ;
boolean p l a y e r T u r n = f a l s e ;

To keep track of the state of the game, Ive declared an 8x8 array, named
arrState. The array will represent the fields of the board. The arrState-array
will only be containing integers, since the board-fields can be in 3 states: Owned
by white (2), owned by black (1) and owned by none (0). The array is initialized
with 0s, and the 4 start pieces are placed by the constructor. This couldnt
be represented by Boolean, which would only state either false or true - and
therefore couldnt represent a blank-field, owned by no players.
Its useful to know when the players have taken 60 turns - and therefore
ends the game - therefore the totalTurns and gameOver is made. TotalTurns
is initialized with the value 0, since the board can keep 64 pieces, and the
game should end after 60 turns, according to Alexandres comments under the
counter()-method. See section 2.1 for details.
One player needs to be the first to act. In Reversi, black always starts,
therefore playerTurn = false.
Once the board is set in use, coordinates to locate friendly and enemy pieces
are vital - in search of valid moves on the board. Therefore the coordinates to
5

search all 8 directions around a field, are declared (see listing 3, below). These
coordinates are relative coordinates, meaning only ranging +1 and -1.
Listing 3: Directions
1
2
3
4
5
6
7
8
9
10
11
12
13

public
public
public
public
public
public
public
public

final
final
final
final
final
final
final
final

Coord
Coord
Coord
Coord
Coord
Coord
Coord
Coord

dirUp = new Coord ( 0 , 1 ) ;


dirDown = new Coord ( 0 , 1 ) ;
d i r L e f t = new Coord ( 1 , 0 ) ;
d i r R i g h t = new Coord ( 1 , 0 ) ;
d i r U p L e f t = new Coord ( 1 , 1);
dirUpRight = new Coord ( 1 , 1 ) ;
dirDownLeft = new Coord ( 1 , 1 ) ;
dirDownRight = new Coord ( 1 , 1 ) ;

// Gather them i n an a r r a y
f i n a l Coord a r r D i r e c t i o n s [ ] = { dirUp , dirDown ,
d i r L e f t , d i r R i g h t , d i r U p L e f t , dirUpRight ,
dirDownLeft , dirDownRight } ;

Further explanation will be given, when the directions are put into use, in
the flip-method.

2.1

counter ()

In the beginning of each method, Ill let Alexandre Buisses own comments
explain the methods purpose.
Alexandre Buisses comment:
Returns how many moves have been played so far. Since there are
64 possible positions and a game begins with 4 already on the board,
a game ends when the counter reaches 60.
This method should only return the count of moves done so far. For this Ill
return the public totalTurns variable. See below in listing 4.
Listing 4: Counter-method
1
2
3

public i n t c o u n t e r ( ) {
return t o t a l T u r n s ;
}

2.2

score ()

Alexandre Buisses comment:


The current score. Simply count how many pieces of each colour
have been placed on the board. You should return an int array of
which the first element is the number of white pieces and the second
is the number of black pieces currently on the board.
To count how many pieces, each player has on the board, we need to run
through the arrState (array of the board). Every time we meet a black or a
6

white piece, well increment that players score by 1. Below, Ive written pseudo
code for the score-method.
White and black score = 0
For all lines in the board-array.
For all the elements (fields).
If this contains a black piece
increment black score with 1
If this contains a white piece
increment white score with 1
Put both scores in a result-array
Return result-array
The final code is located in appendix A.

2.3

printState ()

Alexandre Buisses comment:


This should print the current state of a game on the console, including what the board looks like, what the current score is and how
many moves have been played so far. It will be used mostly by yourself for debugging, and will be replaced by a (hopefully) much nicer
looking GUI once you have completed assignment 2.
There are 3 steps.
1. ...print the current state of the game on the console, including what the
board looks like...
2. ...current score...
3. ...how many moves have been played so far.
The current score will be printed from the return value, of the score-array,
from the score-method, explained at section 2.2.
How many moves, which has been made, is just printing the return value of
the method counter, explained at section 2.1.
Ill briefly run through how I printed out the double array, arrState, introduced at section 2.
The board is divided into y-lines, and x-lines. Exactly like a 2D-coordinatesystem. See figure 2.

Figure 2: BasicBoards coordinates


To print out every fields value, we have to run through every line - and for
every line, we have to run through every field. The direction of which we are
printing out, is shown in figure 3. From (0,0) to (7,7).

Figure 3: BasicBoards printout


The final code is located in appendix A.

2.4

play ()

Alexandre Buisses comment:


The most important method: play a new piece on the board. You
should ensure that this is really a valid move (check the rules to know
the exact definition of what a valid move is) and then add it to the
board, remembering to make all the necessary reversing of opponent
pieces.

There are 2 interesting steps in this method.


1. ...ensure that this is really a valid move...
2. ...reversing of opponent pieces.
Below, Ill explain how i solved these problems.
2.4.1

isValidMove ()

To make sure its a valid move, Ill call a helper method called isValidMove,
with the parameters Coord move and Boolean player. The isValidMovemethod will check if the move follows the rules of Reversi, and return a Boolean
result, to notify the play-method. The rules for a valid move in Reversi, is
simple. The field (which the player is trying to acquire), has to:
1. Be empty.
2. Have an enemy piece next to it, in either of its 8 directions. See figure 4.

Figure 4: Rules of Reversi explained


3. Have a friendly piece in the direction, AFTER the enemy piece. See
figure figure 4, which shows a valid move (3,3), for black, since its 1) an empty
field, 2) has an enemy piece next to it, and 3) has a friendly piece (0,3) in the
direction, AFTER the enemy piece. Had (1,3) been empty, it wouldnt be a
valid move. The following pseudo code can be made:
If move points to a non-empty in the board-array
Return false;
Else
For all directions around the coordinate
If move+1 field in the direction, are inside the board
If the field in that direction is an enemy
While inside the board; search the direction
If empty field
9

Break
If friendly piece met
Return true
To test this pseudo code, lets run it on the figure 4.
Move is (3,3). Our boolean player-value is false (black).
1. (3,3) is an empty field on the board, so we can continue.
2. For all the fields next to (3,3), only one direction has an enemy (2,3).
3. As long as we are inside the board, we are going to search for an friendly
piece. If we meet an empty field, we stop searching.
4. (1,2) has no interest for us, since its neither empty or friendly.
5. Found a friend at (0,3), its a valid move - return true!
Its vital to make sure the search happens inside the board, to avoid array
going Out of bounds. E.g. (9,10).
The full written code for the helper method, isValidMove, can be seen in
appendix A.
2.4.2

flip ()

This method is being called after the isValidMove-method has made sure the
users move is valid. The users integer color has been placed in the array, at the
requested field. Now we need to flip the enemy pieces, which are between the
newly owned field - and our already owned fields.
Lets use the figure 4 again. We have acquired field (3,3). Since we have
already have (0,3), field (1,3) and (2,3) now needs to be made ours (slang:
Flipped ). The following pseudo code can be made:
For all directions around the acquired coordinate.
If move+1 field in the direction, are inside the board.
If the field in that direction is an enemy.
The direction has potential for flipping.
If a direction has potential.
While inside the board; search the direction
If empty field
Break
If friendly piece
Flip everything from acquired field, to friend.
Lets test it on figure 4.
1. For all the fields next to (3,3), only one direction has an enemy (2,3). This
direction has potential!
10

2. Since the direction has potential; searching for empty field or friendly
3. (1,2) has no interest for us, since its neither empty or friendly.
4. Found a friend at (0,3)! Everything from (3,3) to (0,3) is now ours.
The full written code for the helper method, flip, can be seen in appendix
A. The code can be really hard to read, since its using a bit math, so heres a
more technical description.
The current position is at (X,Y) or (3,3), if we use the example at figure
4. The direction, which gives a potential = true, is (-1,0) (left) (xDir = -1,
yDir = 0). Now we start going to the left, from (3,3). Every time we take
one jump left, we increment the jump-variable with one. If we should meet a
friendly piece, we know that everything from (3,3) (the start position) and the
newly found friendly piece, is now ours. The newly found piece should be at
(x+(jump*xDir), y+(jump*yDir)).
The full code of the play-method, can be seen in appendix A

Assignment 2: Board

Assignment 2 consists of the Board-class. The goal for the class, is to contain
one method: validMoves, which will be returning an ArrayList filled with
absolute coordinates, which are valid for a specific player, e.g. black. The class
will implement the Board, which are imported from the Interfaces-package, for
more information, see section 1.1.

3.1

validMoves ()

Alexandre Buisses comment:


This method should return the list of all valid moves for the player
colour on the current state. It will be very useful for assignment 3,
and it may be worth spending time to make sure that it runs as fast
as possible.
This method shouldnt be confused with isValidMove, where we checked
a specific location. Having said that, we will take use of it. See pseudo code
below, how ValidMoves will be working:
For all lines in the board-array.
For all the elements (fields).
Call isValidMove, to check the coordinate
If the return value is true
Save the coordinate, since its a valid move
Return coordinates we gathered.
The code can now come together. See appendix B for full code.
11

Assignment 3: Player

Assignment 3 will allow a human player, to interact with a GUI. Wherever the
user clicks, on the GUI, will indicate his next play.
Each human player will have a GUI, and will keep his/hers own internal
representation of the board and update it whenever a move has been played.
The assignment 3 section will be divided into 2 subsections, one explaining
the player - and one the interface, the player will be interacting with.
The player is the essence to the GUIs interaction, therefore Ill shortly explain how its been put together. The full code, for JlniPlayer and JlniReversiBoard (GUI), can be seen in appendix C and D.

4.1
4.1.1

The Player
init()

Quoted from the assignment:


Init will be called by the referee with an indication of which colour
the player has.
Itll be a simple set-method, which sets the boolean-value for the players
color. False = black, true = white. Thats why Ive created a public variable
called playerColor. At the same time, Im storing the enemys/friendly integer,
e.g. if the player is black (false), the enemy int is 2. This variable will be used
later on in other methods.
The init-methods job is also to send the initial information to the GUI,
and tell it to build/show visible. The information sending is done by a helper
method, which sends the internal board to the GUI - the methods name is
sendInfoToGUI(). I use the init-method to set the GUI visible, since theres no
reason for the user to see the GUI, before hes assigned a player color. By doing
this, I avoid 2 black players (playerColor is false by default), to enter the game,
and make illegal moves.
4.1.2

name()

Quoted from the assignment:


Name should consist of a single statement returning a unique name
for your player, to be used in debugging and on the interface.
The method will return playerName, a public variable on the JlniPlayerclass.
4.1.3

move()

Quoted from the assignment:

12

Move asks the player for his next move. It should return a coordinate object or throw the exception NoPossibleMove. If the player
returns an invalid move, he will be considered as having lost the
game.
The method should return a coordinate object, or throw exception NoPossibleMove.
The algorithm can be written as followed:
1. First we need to reset the previous move, if anything has been moved
previously. The public variable playerMove, located on the GUI, contains
the coord-object, containing the X- and Y-coordinate for the move.
2. Check if the player has any valid move - using the validMoves()-method,
with parameter depending on his playerColor.
3. If the size of the returned array is 0, we have no moves and therefore
throws an exception: NoPossibleMove.
4. ...else we wait for an input from the player.
5. When the user has clicked on the GUI, well use the play()-method written
in JlniBasicBoard, which will store the coordinate in the arrState-array.
6. Send GUI the new information.
7. Redraw the board.
8. Send referee the coordinate.
Please notice, we are not changing whos turn it is to act - thats because
the board, will update its playerTurn-variable, when the play-method is called.
This information is being passed along with all other information, when we hand
the GUI our internal representation of the board.
4.1.4

notify()
Notify will be called by the referee to let the player know what his
opponent has played.

The coordinate in this method, is the latest move from the players opponent.
The coordinate will be stored with the play-method, on our internal representation of the board, then sent to the GUI, with the notice of redrawing the
GUI.

13

4.2

The GUI

The GUI has to live up to some criterias:


1. The GUI needs to show which are the valid moves in a current situation...
2. ...what the scores are...
3. ...and whose turn is next.
4. It should also display an error message if the user clicks on an invalid
position and allow him to make a new choice.
To solve this, Ill use some methods to:
1. buildGUI - Build the graphical representation of the board itself. This
build will take care of the first 3 steps, of the criterias.
2. invalidMoveMessage - For the last step, to show a message, when an
illegal move has been made. To know which field the user presses on, Im
using the mouseClicked-method.
4.2.1

buildGUI

The layout will be done with a LayoutManager, which will be done with a
GridLayout of 8 by 8 squares. To see the difference between the squares of the
board, I want the squares the be different colors - like on a chess board. Of
course the colors cant be black and white, since thats what the pieces are. In
stead Ill use gray-tones. A sketch of the how the GUI should look like, once
finished, can be viewed on figure 5.

14

Figure 5: GUI-sketch
The GUI will need 3 main JPanels, the scorePanel (north panel), board
(center panel) and toActPanel (south panel). The GUI wont be resizable, and
the dimensions will be set according to the users screen-resolution:
Dimension dim = T o o l k i t . g e t D e f a u l t T o o l k i t ( ) . g e t S c r e e n S i z e ( ) ;
Dimension p r e f e r r e d S i z e = new Dimension ( dim . h e i g h t / 2 , dim . h e i g h t / 2 ) ;

To know where the game-pieces will be at any given time, the GUI will be
fed with the players internal board. This will constantly be updated from the
JlniPlayer-class.
At every square/field, Ill place a JLabel, which will be painted. The JPanelobject will be initialized by JlniSquare - full code can be seen in appendix E.
Every field of the board will have a JLabel, which will be painted on. There
will be 4 different fields:
1. Blank field.
2. Valid move (red dot).
3. Black piece.

15

4. White piece.
Ive dimensioned everything in comparison to which resolution the user will
be running. E.g.:
Dimension dim = T o o l k i t . g e t D e f a u l t T o o l k i t ( ) . g e t S c r e e n S i z e ( ) ;
g2 . f i l l O v a l ( dim . h e i g h t / 7 5 , dim . h e i g h t / 7 5 , dim . h e i g h t / 3 5 , dim . h e i g h t / 3 5 ) ;

4.2.2

mouseClicked

Since the fields of the board are made of JPanels, and not JButtons, Ive used
mouseClicked, instead of using an actionListener.
The mouseClicked()-method does 4 jobs:
1. Finds out which field the user clicks on.
2. Saves it as a coordinate.
3. If its an invalid coordinate, a message needs to be shown (calling invalidMoveMessagemethod).
4. If it isnt invalid, itll be saved in the playerMove-variable.
Ive taken into account, that clicking on your valid moves, when its the
opponents turn, shouldnt bring up a message, saying Invalid move!. Instead
Ive chosen for it to do nothing - too many popups can feel annoying to the
player.

4.3

Human vs. Human

Its now possible for 2 humans to play versus each other. All which are needed,
is an referee, who initializes the players (giving them a color) and takes turn,
asking them for a move. Ive created this small referee-class for testing, called
HumanVsHuman - Only meant for testing. It creates 2 players (which creates 2
GUIs), and allows them to take turns, until the game is over. Any of the other
students, would now have the possibility to come by, with their own GUI, and
play against me.

Assignment 4: ComputerPlayer

Since its very hard to find out which is the most efficient evaluation-method, I
designed multiple bots and let them compete against each other, each evaluating
the value of the board differently. By doing this evolutionary-progress, I found
the fittest evaluation method.
Since I was new to Reversi, I didnt know how to value the board, in different
situations, other than the logical way: Get the most pieces. The different

16

strategies used in the evaluation method, were found on several different Reversiforums1 and guidelines2 for Reversi.
Heres a summarizing:
1. Go for the maximum points. E.g. if you can flip 3 of the opponents pieces,
do it instead of flipping 1 piece.
2. Go for the best mobility, meaning go for the move, which gives us the
most valid moves.
3. Take corner, if you can. The corners cant change owners, once acquired
- and is a great step stone, to flip the maximum amount of enemy pieces.
4. Take the sides, they are hard to flip, once taken.
5. Avoid taking the fields next to the corners, it might open up for your
opponent to take the corner before you.
These strategies will be implemented in the AI.

5.1

Structure

The JlniComputerPlayer-class is exactly like JlniPlayer is many ways. They


both have an internal representation of the board, playerColor, etc. The only
real change is the calculations, to come up with a coordinate, which the bot has
to return, when asked for a move by the referee (move() is called). Of course
the AI doesnt need to initialize a GUI, to play on.
Since Reversi is a zero-sum game, the AI will be using the minmax-algorithm3 ,
with alpha-beta pruning, which will help searching deeper.
5.1.1

Minimax

Before going into technical details, Ill make an example of the Minimax (or
minmax) algorithm, which I will refer to, as I explain the implementation.
The minmax-algorithm is designed to look a head, while searching depth
first in a game tree, and pick the best possible move (with the given information),
knowing that our opponent wants to do what is bad for us. The algorithm
is recursive, meaning that the algorithm uses a technique where, in order to
accomplish a task, calls itself with some part of the task4 .
To explain how the algorithm works, Ill refer to figure 65 , which shows a
hypothetical search tree.
Its our turn to act, we are in the current-node, which represents the board
as it is now. The current-node is the board as it stands, when the decision
1 http://www.gamblinggates.com
2 http://en.wikipedia.org/wiki/Reversi
3 http://en.wikipedia.org/wiki/Minmax
4 http://www.itl.nist.gov/div897/sqg/dads/HTML/recursion.html
5 http://www.softwarefederation.com/csci4448/download/minimax.pdf

17

has to be made. We have to choose between 2 options A or B - for now dont


read the integers, only notice we are in a maximizing state, meaning we want
to pick whichever is best for us.
After our choice, we know that its the opponents turn (A-node), and he
wants to pick whatever is the worst for us (minimizing), to leave us with the
minimum possible value - and so on.
A simple minimax algorithm (without alpha-beta pruning) will, in the end,
evaluate all 16 leaf nodes, and then work backwards, opponent minimizes and
we maximizes.
In our position, in the top-node, with the current board, the A-node would
be the best possible move, since it would (if our opponent minimizes, at all
times) leave us in the bottom left node (10). From there, G minimizes (picks
10 over 11), C maximizes, and A minimizes.
The numbers inside the search nodes, are the value of the board in that
position, for the maximizing players perspective. The value differ from game to
game, this will be discussed in section 5.3.
This is the first build, for my bot, to make it able to build the search tree
- and evaluating which is the best move. In the next section, Ill implement
alpha-beta pruning.

Figure 6: Search tree, with depth of 4.


5.1.2

Alpha-beta pruning

The alpha-beta pruning helps cutting off the leafs, which arent needed. By
doing this, we can reach the same levels, but at a much faster time. The alpha
beta approach is illustrated in figure 7, which shows the left part of the previous
search tree.
At the C node, after searching the value of the G-node, we know that since G
has a value of 10, node H needs to bigger than 10 to be picked. When searching
Hs first left-child, we know that Hs value will be maximum 9, so Hs right
child-node doesnt matter, and we can cut it off
Pruning happens again in the D node. Since A is minimizing, we have to
find a value on the D-node, which is less than 10 - else A wants to pick the
C-node. After searching the I-node, we find that the value 14 will be picked, so

18

the D-node will have a value of at least 14. We now dont have to search the
J-node, since its result wont change As minimizing-decision - and hell end up
picking C (10) instead of D (14).

Figure 7: Search tree, with alpha beta pruning.


By doing this, we allow Minimax to do the same analysis, but more efficiently
- without loosing any information.
Beta is my opponents best current move, and alpha is the node which Im
searching. We can write the following code for pruning:
Listing 5: Pruning at maximizing player
1
2
3

i f ( a l p h a >= b e t a ) {
return a l p h a ;
}

Listing 6: Pruning at minimizing player


1
2
3

i f ( a l p h a <= b e t a ) {
return a l p h a ;
}

To put this code into context, lets look at node H again, in figure 7. The
beta value, which is past to him, is 10 (currently the best pick in the C node),
and the alpha value is 9 - his first child. Since H is a minimizing state, we need
to find an alpha, which are smaller than our beta, in order to prune. In this
case it is, and we can break (stop searching).
At the D-node on the other hand, its the maximizing players turn. Our beta
is 10 (from C node), and alpha is 14 (I-node). Since our alpha is now larger
than our beta, we can stop searching.
The algorithm can now be implemented into the computer player. To make
the code easier to read and understand, Ive divided the algorithm into 2 methods - one maximizing (myTurn) and one minimizing (hisTurn).

19

5.2

move()

When asked, by the referee, the AI should reset his previous move, and starts
calculating for the next. If he doesnt up with a move (e.g. if theres no moves
to be made), he should throw an exception NoPossibleMove - else, if he finds
a move, he will return it to the referee.
When starting the calculations, to find the best possible move, he calls the
myTurn()-method, with the following parameters: myTurn(currentBoard, playerColor, 0, -1000000, +1000000). The currentBoard is the internal representation of the board, the playerColor is the boolean value of the player he is and the last 2 values are the initial alpha and beta values. They are large/low
integers, to make sure they are overridden in the methods.
5.2.1

myTurn()

This method has serveral jobs to perform, when called by both the move()- and
hisTurn()-method. To explain my approach, Ive written pseudo code:
Return board value, if game is over or we reach max depth
For every valid move...
Clone the board.
Perform the move.
Get the value of our children.
(Maximizing) If value is better than alpha...
Replace alpha
If we are at depth 0...
This is our best move!
If alpha is bigger or equal to beta...
Prune by returning alpha!
Return alpha
When we call for our childrens value, we call hisTurn, with the parameters
boardClone, !player, currentDepth+1, beta, alpha. The board clone, which
were created (boardClone), no longer our turn (!player) and we go one level
deeper (currentDepth+1). Alpha and beta will now be reversed, our current
best move (alpha) is now the opponents beta - so that he can match his alpha,
and see if he can prune leafs.
5.2.2

hisTurn()

The hisTurn-method is, of course, very similar to myTurn. Again, this algorithm could be written in the same method as myTurn - but doing it this way,
allows me to better explain what happens.
Pseudo code:
Return board value, if game is over or we reach max depth

20

For every valid move...


Clone the board.
Perform the move.
Get the value of our children.
(Minimizing) If value is less than alpha...
Replace alpha
If alpha is less or equal to beta...
Prune by returning alpha!
Return alpha
Again we invert the alpha- and beta-values, when calling the myTurn-method,
for the value of our children. Notice that we wont update the best possible
move, if we are at depth 0 - since only our decision can be located at depth 0.

5.3

boardValue()

While the minimax algorithm, with alpha beta pruning, can become hard to
control - due to the search trees size and the recursive calls - the value of the
board is easier to grasp.
The boardValue-method should return an integer, which represents the value
of the board, seen from the perspective of the current player. Theres no best
way to value a board, itll just represent the bots style of play (its personality).
One bot might value the corners high and mobility low, another values the board
opposite, etc.
But before implementing the strategy in this static board evaluator, we wish
to let it know, that if the game is over, we can simply return infinite (if we won)
or -infinite (if we lost). By doing this, we make sure that the bot will always
pick this option, when maximizing over any other node.
After doing the above, we can implement the strategy explained in section
5:
1. Go for the maximum points. E.g. if you can flip 3 of the opponents pieces,
do it instead of flipping 1 piece.
2. Go for the best mobility, meaning go for the move, which gives us the
most valid moves.
3. Take corner, if you can. The corners cant change owners, once aquired and is a great stepstone, to flip the maximum amount of enemy pieces.
4. Take the sides, they are hard to flip, once taken.
5. Avoid taking the fields next to the corners, it might open up for your
opponent to take the corner before you.
To implement the strategy above, Ive assigned points, depending where the
owned pieces are located on the board.

21

1. By assigning +1 point for every owned piece on the board, the value of
the board will increase, as we gain the most pieces.
1
2
3
4
5

i f ( p l a y e r C o l o r == f a l s e ) {
result = result + score [ 1 ] ;
} else {
result = result + score [ 0 ] ;
}

2. For every valid move we have, on the evaluated board, we assign 3 points
- the bot will now evaluate mobility higher than just gaining points.
1
2
3
4
5

A r r a y L i s t <Coord> v a l i d M o v e s = board . v a l i d M o v e s ( p l a y e r C o l o r ) ;
int m o b i l i t y = validMoves . s i z e ( ) ;
...
// M o b i l i t y
result = result + ( mobility 3);

3. If we own a corner, we assign +50 points to result. This make the bot
rate corners higher than everything mentioned above.
To do this, Ive made an ArrayList of corner-coordinates, which I check
for the players integer. Ie. black = 1, white = 2. I wont show the code
here, since its too big for the page - for full code, please view appendix
F.
4. Like the corners, we need to assign additional value for the sides. That
means every field leading up to the edge of the board - minus corners and
the fields leading up to the corners. The coordinates are listed below - and
can be view on figure 8. For every coordinate we own, we assign result
+5.

Figure 8: Board sides, evaluated +5.

22

1
2
3
4
5
6

Coord a r r S i d e s [ ] = { new Coord ( 0 , 2 ) , new Coord ( 0 , 3 ) ,


new Coord ( 0 , 4 ) , new Coord ( 0 , 5 ) , new Coord ( 7 , 2 ) ,
new Coord ( 7 , 3 ) , new Coord ( 7 , 4 ) , new Coord ( 7 , 5 ) ,
new Coord ( 2 , 0 ) , new Coord ( 3 , 0 ) , new Coord ( 4 , 0 ) ,
new Coord ( 5 , 0 ) , new Coord ( 2 , 7 ) , new Coord ( 3 , 7 ) ,
new Coord ( 4 , 7 ) , new Coord ( 5 , 7 ) } ;

5. Avoid taking the fields next to the corners, it might open up for your
opponent to take the corner before you.
This one is interesting! On the earlier bots, I designed, I tried assigning
-10, to each field next to the corners, e.g. (1,0), (1,1) and (0,1). When I tried
letting it play against a computer opponent (itself), they were really good at
getting the corners, but once a corner (e.g. 0,0) was taken, they still feared the
surrounding fields. So of course, the bot should understand it like this:
Pseudo code:
For every corner
If the corner is free
- 10, for each piece we have in the fields next to the corner
In the end, the method returns the result-integer, showing how strong or
weak we are, on the board.

5.4

Human vs. Bot

Its now possible for a human to play against a bot. The bot reacts just like a
human, and sends back an absolute coordinate, once requested by the referee.
Again Ive made a HumanVsBot-class, which allows a human to compete against
a computer player. The computer players max depth ended up being 6 - and
acts before 8 seconds each time.
If I had more time, I would have loved to changed something about the
computer player. When you play against it several times, you notice it keeps
playing the same pattern. I.e. if its white, and human is black - human starts
out by moving to (3,2), the computer player will always move to (4,2). Of
course that makes sense, since its just based on calculations, and always will
come up with the same result. But to remove this same opening pattern, I
could implement it to make a random pick, when the result of the best children
are the same. I.e. the best move is 19, but theres another move, which also has
the value 19 - why not pick randomly between those? As it stands right now,
the first value of 19, will be picked as the best possible move, and creates the
same opening.

5.5

Bot vs. Bot

In the BotVsBot-class, Its possible to allow 2 computer players to compete. I


build this class, to see which of my evaluation functions were best - but its also
23

a great testing ground!

24

JlniBasicBoard.java
Listing 7: JlniBasicBoard.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

/ O t h e l l o B a s i c Board /
/ C r e a t e d by Jon Lau N i e l s e n /
package j l n i ;
import I n t e r f a c e s . ;
public c l a s s J l n i B a s i c B o a r d implements BasicBoard {
public boolean gameOver = f a l s e ;
public i n t t o t a l T u r n s = 0 ;
public i n t a r r S t a t e [ ] [ ] = new i n t [ 8 ] [ 8 ] ;
public
public
public
public
public
public
public
public
public

boolean p l a y e r T u r n = f a l s e ;
f i n a l Coord dirUp = new Coord ( 0 , 1 ) ;
f i n a l Coord dirDown = new Coord ( 0 , 1 ) ;
f i n a l Coord d i r L e f t = new Coord ( 1 , 0 ) ;
f i n a l Coord d i r R i g h t = new Coord ( 1 , 0 ) ;
f i n a l Coord d i r U p L e f t = new Coord ( 1 , 1);
f i n a l Coord dirUpRight = new Coord ( 1 , 1 ) ;
f i n a l Coord dirDownLeft = new Coord ( 1 , 1 ) ;
f i n a l Coord dirDownRight = new Coord ( 1 , 1 ) ;

// Gather them i n an a r r a y
f i n a l Coord a r r D i r e c t i o n s [ ] = { dirUp , dirDown ,
d i r L e f t , d i r R i g h t , d i r U p L e f t , dirUpRight ,
dirDownLeft , dirDownRight } ;
/@ S p e c i f i c a t i o n f o r method i n t P l a y e r
C o n v e r t s a p l a y e r s b o o l e a n v a l u e t o h i s boar d
r e p r e s e n t a t i o n . Black = 1 , white = 2.

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .
@ o u t p u t : \ r e s u l t i s e i t h e r 2 or 1 .
/
public i n t i n t P l a y e r ( boolean b o l P l a y e r ) {
int r e s u l t = bolPlayer ? 2 : 1 ;
return r e s u l t ;
}
/@ S p e c i f i c a t i o n f o r method s c o r e
Returns an i n t array , w i t h t h e s c o r e o f t h e two p l a y e r s

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a u s e r .

f a l s e == b l a c k , and t r u e == w h i t e .
@ o u t p u t : \ r e s u l t i s an i n t a r r a y w i t h [ 0 ] as w h i t e

s c o r e and [ 1 ] as b l a c k s s c o r e .
/
public i n t [ ] s c o r e ( ) {
i n t r e s u l t [ ] = new i n t [ 2 ] ;
int scoreBlack = 0 ;
int scoreWhite = 0 ;

25

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

f o r ( i n t y = 0 ; y < a r r S t a t e . l e n g t h ; y++){
f o r ( i n t x = 0 ; x < a r r S t a t e [ y ] . l e n g t h ; x++){
i f ( a r r S t a t e [ x ] [ y ] == 1 ) {
s c o r e B l a c k ++;
}
i f ( a r r S t a t e [ x ] [ y ] == 2 ) {
s c o r e W h i t e ++;
}
}
}
r e s u l t [ 1 ] = scoreBlack ;
r e s u l t [ 0 ] = scoreWhite ;
return r e s u l t ;
}
/@ S p e c i f i c a t i o n f o r method c o u n t e r
Returns t h e v a r i a b l e t o t a l T u r n s .

@ I n p u t :
Nothing
@ Output : \ r e s u l t >= 0
/
public i n t c o u n t e r ( ) {
return t o t a l T u r n s ;
}
/@ S p e c i f i c a t i o n f o r method f l i p
F l i p s a l l newly a c q u i r e d p i e c e s .

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .

Coord ( a b s o l u t e c o o r d i n a t e ) i s t h e

newly p l a c e d p i e c e . Coord <> n u l l .


@ o u t p u t :
None
/
public void f l i p ( Coord move , Boolean p l a y e r ) {
i n t enemy = i n t P l a y e r ( ! p l a y e r ) ;
i n t me = i n t P l a y e r ( p l a y e r ) ;
i n t x = move . getX ( ) ;
i n t y = move . getY ( ) ;
f o r ( i n t i =0; i <a r r D i r e c t i o n s . l e n g t h ; i ++){
Coord c o o r d D i r e c t i o n = a r r D i r e c t i o n s [ i ] ;
i n t xDir = c o o r d D i r e c t i o n . getX ( ) ;
i n t yDir = c o o r d D i r e c t i o n . getY ( ) ;
boolean p o t e n t i a l = f a l s e ;
// I f we a r e i n s i d e t h e boar d
i f ( ( y+yDir ) > 1
&& ( y+yDir ) < 8
&& ( x+xDir ) < 8
&& ( x+xDir ) > 1 ) {
// I f we have an enemy n e x t t o us , i n t h e d i r e c t i o n we a r e g o i n g .
i f ( a r r S t a t e [ x+xDir ] [ y+yDir ] == enemy ) {

26

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

//Then t h e d i r e c t i o n has p o t e n t i a l
p o t e n t i a l = true ;
}
}
i f ( p o t e n t i a l == true ) {
i n t jump = 2 ;
while ( ( y+(jump yDir ) ) > 1
&& ( y+(jump yDir ) ) < 8
&& ( x+(jump xDir ) ) < 8
&& ( x+(jump xDir ) ) > 1 )
{
// Let s s e e i f i can f i n d a f r i e n d
i f ( a r r S t a t e [ x+(jump xDir ) ] [ y+(jump yDir ) ] == 0 ) {
break ;
}
i f ( a r r S t a t e [ x+(jump xDir ) ] [ y+(jump yDir ) ] == me) {
// Great ! We found a mate , e v e r y t h i n g b e t w e e n (X,Y) , and
// ( x+jump xDir , y+jump yDir ) i s o u r s !
//K = 1 , s i n c e 0 i s our own b u t t o n .
f o r ( i n t k = 1 ; k < jump ; k++){
a r r S t a t e [ x+k xDir ] [ y+k yDir ] = me ;
}
break ;
}
jump++;
}
}
}
}

/@ S p e c i f i c a t i o n f o r method isGameOver
Checks i f t h e game i s over , and u p d a t e s t h e
gameOverv a r i a b l e .

@ i n p u t :
Nothing
@ o u t p u t :
Nothing
/
public void isGameOver ( ) {
i n t count = 0 ;
// F i n d i n g how many v a l i d M o v e s b o t h p l a y e r s have ,
//We don t have t h e v a l i d M o v e s () method y e t !
f o r ( i n t y = 0 ; y <8; y++){
f o r ( i n t x = 0 ; x <8; x++){
i f ( i s V a l i d M o v e (new Coord ( x , y ) , f a l s e ) == true ) {
count++;
}
i f ( i s V a l i d M o v e (new Coord ( x , y ) , true ) == true ) {
count++;
}
}
}
i f ( count == 0 ) {
gameOver = true ;
}
}

27

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

/@ S p e c i f i c a t i o n f o r method p l a y
Let s a u s e r p l a c e a p i e c e on t h e board , i f i t s
a l e g a l move .

@ I n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .

Coord ( a b s o l u t e c o o r d i n a t e ) i s t h e

r e q u e s t i n g move . Coord <> n u l l .


@ Output :
Nothing
/
public void p l a y ( Coord move , boolean p l a y e r ) throws I l l e g a l M o v e {
int i n t P l a y e r = i n t P l a y e r ( p l a y e r ) ;
boolean bolValidMove = i s V a l i d M o v e ( move , p l a y e r ) ;
//He can o n l y move , i f t h e game i s n t over ,
// and i t i s n t an i n v a l i d move .
i f ( bolValidMove == f a l s e | | gameOver == true ) {
throw new I l l e g a l M o v e ( move ) ;
} else {
a r r S t a t e [ move . getX ( ) ] [ move . getY ( ) ] = i n t P l a y e r ;
f l i p ( move , p l a y e r ) ;
playerTurn = ! playerTurn ;
t o t a l T u r n s ++;
isGameOver ( ) ;
}
}
/@ S p e c i f i c a t i o n f o r method i s V a l i d M o v e

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .

Coord ( a b s o l u t e c o o r d i n a t e ) i s t h e

r e q u e s t i n g move . Coord <> n u l l .


@ o u t p u t :
The r e s u l t t e l l s i f i t s a v a l i d move

or n o t . \ r e s u l t i s e i t h e r t r u e or f a l s e
/
public boolean i s V a l i d M o v e ( Coord move , boolean p l a y e r ) {
boolean r e s u l t = f a l s e ;
i n t enemy = i n t P l a y e r ( ! p l a y e r ) ;
i n t me = i n t P l a y e r ( p l a y e r ) ;
i n t x = move . getX ( ) ;
i n t y = move . getY ( ) ;
// Check i f t h e f i e l d i s b l a n k .
i f ( a r r S t a t e [ x ] [ y ] != 0 ) {
return f a l s e ;
} else {
//Okay so t h e f i e l d i s b l a n k .
// Let s g e t t h e d i r e c t i o n s
f o r ( i n t i =0; i <a r r D i r e c t i o n s . l e n g t h ; i ++){

28

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

//The d i r e c t i o n i s s t o r e d , f o r u s e . . .
Coord c o o r d D i r e c t i o n = a r r D i r e c t i o n s [ i ] ;
// G e t t i n g d i r e c t i o n s
i n t xDir = c o o r d D i r e c t i o n . getX ( ) ;
i n t yDir = c o o r d D i r e c t i o n . getY ( ) ;
i n t jump=2;
// Check i n e v e r y d i r e c t i o n , i f t h e r e s an enemy
i f ( ( y+yDir ) > 1
&& ( y+yDir ) < 8
&& ( x+xDir ) < 8
&& ( x+xDir ) > 1)
{
i f ( a r r S t a t e [ x+xDir ] [ y+yDir ] == enemy )
{
// Search w h i l e i n s i d e t h e boardframe
while ( ( y+(jump yDir ) ) > 1
&& ( y+(jump yDir ) ) < 8
&& ( x+(jump xDir ) ) < 8
&& ( x+(jump xDir ) ) > 1)
{
// Looking f o r a f r i e n d
//Empty s p a c e i s no good
i f ( a r r S t a t e [ x+jump xDir ] [ y+jump yDir ] == 0 ) {
break ;
}
//Found f r i e n d , i t s a l e g a l move !
i f ( a r r S t a t e [ x+jump xDir ] [ y+jump yDir ] == me) {
return true ;
}
jump++;
}
}
}
}
}
return r e s u l t ;
}

/@ S p e c i f i c a t i o n f o r c o n s t r u c t o r J l n i B a s i c B o a r d
The c o n s t r u c t o r i s g o i n g t o p l a c e t h e 4 s t a r t u p p i e c e s on t h e
8 x8 boar d . In c o o r d i n a t e s : White : ( 3 , 3 ) ( 4 , 4 ) . B l a c k : ( 4 , 3 ) ( 3 , 4 ) .

@ i n p u t :
Nothing .
@ o u t p u t : Nothing
/
public J l n i B a s i c B o a r d ( ) {
// S e t t i n g s up t h e 4 s t a r t u p p i e c e s .
arrState [ 3 ] [ 3 ] = 2;
arrState [ 4 ] [ 4 ] = 2;
arrState [ 4 ] [ 3 ] = 1;
arrState [ 3 ] [ 4 ] = 1;
// t o t a l T u r n s was i n i t i a l i z e d w i t h t h e v a l u e 0 .
}

29

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

/@ S p e c i f i c a t i o n f o r method p r i n t S t a t e
p r i n t S t a t e () method i s p r i n t i n g o u t t h e c u r r e n t s t a t e o f t h e game .
The bo ard i s draw from t h e a r r a y a r r S t a t e .

@ i n p u t :
Nothing .
@ o u t p u t :
Nothing .
/
public void p r i n t S t a t e ( ) {
i n t a r r S c o r e [ ] = new i n t [ 2 ] ;
// F i n d i n g who s t u r n i t i s ( as I n t e g e r ) ,
// w i t h t h e h e l p from i n t P l a y e r () method
String strPlayerTurn ;
i f ( p l a y e r T u r n == f a l s e ) {
s t r P l a y e r T u r n = Black ;
} else {
s t r P l a y e r T u r n = White ;
}
f o r ( i n t y = 0 ; y < a r r S t a t e . l e n g t h ; y++)
{
f o r ( i n t x = 0 ; x < a r r S t a t e [ y ] . l e n g t h ; x++)
{
// P r i n t a w a l l t o b e g i n w i t h .
System . out . p r i n t ( | ) ;
// P r i n t o u t what s i n t h e a r r a y a t [ x ] [ y ] , i f t h e f i e l d i s
// owned by a p l a y e r .
i f ( a r r S t a t e [ x ] [ y ] == 1 | | a r r S t a t e [ x ] [ y ] == 2 ) {
System . out . p r i n t ( a r r S t a t e [ x ] [ y ] ) ;
}
i f ( a r r S t a t e [ x ] [ y ] == 0 ) {
// I f i t i s n t owned by a p l a y e r , t h e n i t s a b l a n k f i e l d .
System . out . p r i n t ( ) ;
}
}
System . out . p r i n t l n ( | ) ;
}
System . out .
arrScore =
System . out .
System . out .

p r i n t ( Current s c o r e : ) ;
score ( ) ;
p r i n t ( Black : + a r r S c o r e [ 1 ] ) ;
p r i n t l n ( , White : +a r r S c o r e [ 0 ] ) ;

System . out . p r i n t l n ( Counter : + c o u n t e r ( ) ) ;


System . out . p r i n t l n ( Turn : + s t r P l a y e r T u r n ) ;
}
}

30

JlniBoard.java
Listing 8: JlniBoard.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

/ O t h e l l o Board /
/ C r e a t e d by Jon Lau N i e l s e n /
package j l n i ;
import j a v a . u t i l . A r r a y L i s t ;
import I n t e r f a c e s . ;
public c l a s s J l n i B o a r d extends J l n i B a s i c B o a r d implements Board {
/@ S p e c i f i c a t i o n f o r method v a l i d M o v e s
The method , v a l i d M o v e s , w i l l r e c e i v e
a s i n g l e b o o l e a n , and r e t u r n an A r r a y L i s t ,

f i l l e d w i t h a l l v a l i d moves , f o r t h e p l a y e r .

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .
@ o u t p u t : A r r a y L i s t , w i t h v a l i d , a b s o l u t e , c o o r d i n a t e s .
/
public A r r a y L i s t <Coord> v a l i d M o v e s ( boolean c o l o r ) {
A r r a y L i s t <Coord> a r r V a l i d C o o r d s = new A r r a y L i s t <Coord > ( ) ;
// Running t h r o u g h t h e array , t o f i n d v a l i d c o o r d s .
f o r ( i n t y = 0 ; y<a r r S t a t e . l e n g t h ; y++){
f o r ( i n t x = 0 ; x<a r r S t a t e [ y ] . l e n g t h ; x++){
// S e t t i n g up t h e Coord f o r t e s t i n g
Coord t e s t C o o r d = new Coord ( x , y ) ;
// T e s t i n g i f i t s a v a l i d move
Boolean v a l i d = i s V a l i d M o v e ( t e s t C o o r d , c o l o r ) ;
i f ( v a l i d == true ) {
// I f i t s a v a l i d coord , we add i t
a r r V a l i d C o o r d s . add ( t e s t C o o r d ) ;
}
}
}
//We a r e done , l e t s r e t u r n our r e s u l t .
return a r r V a l i d C o o r d s ;
} // Method
}

31

JlniPlayer.java
Listing 9: JlniPlayer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

/ O t h e l l o P l a y e r /
/ C r e a t e d by Jon Lau N i e l s e n /
package j l n i ;
import j a v a . u t i l . A r r a y L i s t ;
import j a v a x . swing . JFrame ;
import I n t e r f a c e s . ;
public c l a s s J l n i P l a y e r implements P l a y e r {
public
public
public
public
public
public

boolean p l a y e r C o l o r ;
i n t enemyInt ;
i n t meInt ;
S t r i n g playerName = HumanPlayer ;
J l n i R e v e r s i B o a r d GUI ;
J l n i B o a r d c u r r e n t B o a r d = new J l n i B o a r d ( ) ;

/@ S p e c i f i c a t i o n f o r method i n i t
S e t s t h e p l a y e r v a r i a b l e s , s e n d s t h e
i n f o r m a t i o n t o t h e GUI , b u i l d s and shows t h e bo ard

@ i n p u t :
Boolean v a l u e , r e p r e s e n t i n g a p l a y e r .

f a l s e == b l a c k , and t r u e == w h i t e .
@ o u t p u t : Nothing
/
public void i n i t ( boolean c o l o r ) {
playerColor = color ;
meInt = c o l o r ? 0 : 1 ;
enemyInt = ! c o l o r ? 0 : 1 ;
// Updating t h e boa rd
// Send game i n f o r m a t i o n
GUI = new J l n i R e v e r s i B o a r d ( p l a y e r C o l o r ) ;
sendInfoToGUI ( ) ;
GUI . buildGUI ( ) ;
GUI . s e t T i t l e ( R e v e r s i ) ;
GUI . pack ( ) ;
GUI . s e t R e s i z a b l e ( f a l s e ) ;
GUI . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ;
GUI . s e t V i s i b l e ( true ) ;
}
/@ S p e c i f i c a t i o n f o r method name
Gets t h e playerNamev a r i a b l e s , and r e t u r n s i t .

@ i n p u t :
Nothing .
@ o u t p u t : P l a y e r name as s t r i n g .
/
public S t r i n g name ( ) {
return playerName ;
}

32

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

/@ S p e c i f i c a t i o n f o r method move
Move i s c a l l e d by a r e f e r e e , i f no p o s s i b l e
moves , i t t h r o w s a NoPossibleMovee x c e p t i o n .
E l s e a l o o p k e e p s running , w a i t i n g f o r
t h e u s e r t o i n p u t h i s n e x t move .

@ i n p u t :
Nothing .
@ o u t p u t : An a b s o l u t e coord , or t h r o w s

NoPossibleMove .
/
public Coord move ( ) throws NoPossibleMove {
GUI . playerMove = n u l l ;
A r r a y L i s t <Coord> a r r V a l i d M o v e s = n u l l ;
// C a l l i n g ValidMoves ( ) , t o f i n d o u t
// i f we have any v a l i d moves .
arrValidMoves = currentBoard . validMoves ( playerColor ) ;
i f ( a r r V a l i d M o v e s . s i z e ( ) == 0 ) {
throw new NoPossibleMove ( ) ;
} else {
while (GUI . playerMove == n u l l ) {
// w a i t i n g f o r t h e u s e r t o
// g i v e us a c o o r d i n a t e !
}
try {
c u r r e n t B o a r d . p l a y (GUI . playerMove , p l a y e r C o l o r ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;
}
sendInfoToGUI ( ) ;
GUI . redrawBoard ( ) ;
return GUI . playerMove ;
}
}
/@ S p e c i f i c a t i o n f o r method n o t i f y .
n o t i f y i s c a l l e d by a r e f e r e e , t o
l e t t h e p l a y e r know what has been p l a y e d
by t h e opponent . The move w i l l be p l a y e d ,
and t h e i n f o r m a t i o n w i l l be s e n t t o t h e
GUI boa rd .

@ i n p u t : An a b s o l u t e coord .
@ o u t p u t : Nothing .
/
public void n o t i f y ( Coord move ) {
try {
c u r r e n t B o a r d . p l a y ( move , ! p l a y e r C o l o r ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;
}
sendInfoToGUI ( ) ;
GUI . redrawBoard ( ) ;
}

33

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

/@ S p e c i f i c a t i o n f o r method sendInfoToGUI .
H e l p e r method , which w i l l be c a l l e d
from s e v e r a l methods . The method u p d a t e s
t h e GUI w i t h t h e l a t e s t i n f o r m a t i o n .

@ i n p u t :
Nothing
@ o u t p u t : Nothing .
/
public void sendInfoToGUI ( ) {
GUI . c u r r e n t B o a r d = t h i s . c u r r e n t B o a r d ;
i f ( playerColor
GUI . blackName
GUI . whiteName
} else {
GUI . blackName
GUI . whiteName
}

== f a l s e ) {
= playerName ;
= Opponent ;
= Opponent ;
= playerName ;

}
}

34

JlniReversiBoard.java
Listing 10: JlniReversiBoard.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

/ O t h e l l o P l a y e r /
/ C r e a t e d by Jon Lau N i e l s e n /
package j l n i ;
import
import
import
import

j a v a x . swing . ;
Interfaces .;
j a v a . awt . ;
j a v a . awt . e v e n t . ;

public c l a s s J l n i R e v e r s i B o a r d extends JFrame implements M o u s e L i s t e n e r {


public JPanel gfxBoard = new JPanel ( ) ;
public J l n i B o a r d c u r r e n t B o a r d = new J l n i B o a r d ( ) ;
public S t r i n g s e l e c t e d X ;
public S t r i n g s e l e c t e d Y ;
public S t r i n g blackName = ;
public S t r i n g whiteName = ;
public Coord playerMove = n u l l ;
public boolean p l a y e r C o l o r = f a l s e ;
public J l n i R e v e r s i B o a r d ( boolean p l a y e r C ) {
p l a y e r C o l o r = playerC ;
}
/@ S p e c i f i c a t i o n f o r method i n v a l i d M o v e M e s s a g e
Shows a message box , when t h e u s e r c l i c k s
on an i n v a l i d move .

@ i n p u t :
Nothing
@ o u t p u t : Nothing
/
public void i n v a l i d M o v e M e s s a g e ( ) {
JOptionPane . showMessageDialog ( this ,
Sorry , but t h a t s an i n v a l i d move . Try a g a i n ! ,
I n v a l i d move ! ,
JOptionPane .INFORMATION MESSAGE ) ;
}
/@ S p e c i f i c a t i o n f o r method buildGUI
B u i l d s t h e GUI , u s i n g a 8 x8 g r i d l a y o u t ,
f o r t h e b oard . For e v e r y f i e l d on t h e board ,
t h e J l n i S q u a r e c l a s s i s used .

@ i n p u t :
Nothing
@ o u t p u t : Nothing
/
public void buildGUI ( ) {
// C r e a t i n g t h e bo ard i t s e l f
LayoutManager l a y o u t = new GridLayout ( 8 , 8 ) ;
gfxBoard . s e t L a y o u t ( l a y o u t ) ;
f o r ( i n t y =0; y <8; y++) {
f o r ( i n t x =0; x <8; x++) {

35

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

Color bgColor = null ;


// Background o f s q u a r e
i f ( ( x+y ) % 2 == 0 ) {
b g C o l o r = new C o l o r ( 1 5 0 , 1 5 0 , 1 5 0 ) ;
} else {
b g C o l o r = new C o l o r ( 2 0 0 , 2 0 0 , 2 0 0 ) ;
}
boolean validMove =
c u r r e n t B o a r d . i s V a l i d M o v e (new Coord ( x , y ) , p l a y e r C o l o r ) ;
JlniSquare square =
new J l n i S q u a r e ( bgColor , c u r r e n t B o a r d . a r r S t a t e [ x ] [ y ] , validMove , x , y ) ;
square . addMouseListener ( this ) ;
gfxBoard . add ( s q u a r e ) ;
}
}
JPanel
JPanel
JPanel
JPanel

s c o r e P a n e l = new JPanel ( ) ;
t o A c t P a n e l = new JPanel ( ) ;
s c o r e P a n e l R i g h t = new JPanel ( ) ;
s c o r e P a n e l L e f t = new JPanel ( ) ;

int [ ] a r r S c o r e = currentBoard . s c o r e ( ) ;
s c o r e P a n e l L e f t . add (
new J L a b e l ( [ + blackName + ] Black s c o r e : + a r r S c o r e [ 1 ] ) ) ;
s c o r e P a n e l R i g h t . add (
new J L a b e l ( [ + whiteName + ] White s c o r e : + a r r S c o r e [ 0 ] ) ) ;
S t r i n g actionOnPlayer = ;
i f ( c u r r e n t B o a r d . p l a y e r T u r n == f a l s e ) {
a c t i o n O n P l a y e r = Black ;
} else {
a c t i o n O n P l a y e r = White ;
}
t o A c t P a n e l . add (new J L a b e l ( a c t i o n O n P l a y e r + i s n e x t t o a c t ) ) ;
s c o r e P a n e l . add ( s c o r e P a n e l L e f t ) ;
s c o r e P a n e l . add ( s c o r e P a n e l R i g h t ) ;
getContentPane ( ) . add ( gfxBoard , BorderLayout .CENTER) ;
getContentPane ( ) . add ( toActPanel , BorderLayout .SOUTH) ;
getContentPane ( ) . add ( s c o r e P a n e l , BorderLayout .NORTH) ;
Dimension dim = T o o l k i t . g e t D e f a u l t T o o l k i t ( ) . g e t S c r e e n S i z e ( ) ;
Dimension p r e f e r r e d S i z e =
new Dimension ( dim . h e i g h t / 2 , dim . h e i g h t / 2 ) ;
setPreferredSize ( preferredSize );
}

public void a c t i o n P e r f o r m e d ( ActionEvent e ) { }


/@ S p e c i f i c a t i o n f o r method m o u s e C l i c k e d
S e t s t h e playerMove , which t h e move() method
needs to return to a r e f e r e e .

36

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

@ i n p u t : MouseEvent e
@ o u t p u t : Nothing
/
public void m o u s e Cl i c k e d ( MouseEvent e ) {
playerMove = n u l l ;
int x = ( ( Jl niS qu ar e ) e . getSource ( ) ) . x ;
int y = ( ( Jl niS qu ar e ) e . getSource ( ) ) . y ;
Coord move = new Coord ( x , y ) ;
boolean validMove = c u r r e n t B o a r d . i s V a l i d M o v e ( move , p l a y e r C o l o r ) ;
i f ( validMove == f a l s e ) {
invalidMoveMessage ( ) ;
} else {
playerMove = move ;
}
}
/@ S p e c i f i c a t i o n f o r method redrawBoard
Every time new i n f o r m a t i o n has been t o
t h e GUI , i t n e e d s t o be u p d a t e d by
r e d r a w i n g e v e r y t h i n g . I f t h e game i s
over , t h e gameoverMessagemethod w i l l be
called .

@ i n p u t :
Nothing
@ o u t p u t : Nothing
/
public void redrawBoard ( ) {
gfxBoard . r e m o v e A l l ( ) ;
buildGUI ( ) ;
pack ( ) ;
i f ( c u r r e n t B o a r d . gameOver == true ) {
gameoverMessage ( ) ;
}
}
/@ S p e c i f i c a t i o n f o r method gameoverMessage
Shows a message box , i f t h e game i s o v e r .

@ i n p u t :
Nothing
@ o u t p u t : Nothing
/
public void gameoverMessage ( ) {
S t r i n g msg = ;
int [ ] a r r S c o r e = currentBoard . s c o r e ( ) ;
i f ( arrScore [ 1 ] > arrScore [ 0 ] ) {
msg = Winner i s b l a c k ! ;
}
i f (( arrScore [ 1 ] < arrScore [ 0 ] ) ) {
msg = Winner i s w h i t e ! ;
}
i f ( ( a r r S c o r e [ 1 ] == a r r S c o r e [ 0 ] ) ) {
msg = I t s a draw ! ;

37

168
169
170
171
172
173
174
175
176
177
178
179

}
JOptionPane . showMessageDialog ( this ,
msg ,
Game Over ! ,
JOptionPane .INFORMATION MESSAGE ) ;
}
public
public
public
public

void
void
void
void

mouseEntered ( MouseEvent a r g 0 ) {}
mouseExited ( MouseEvent a r g 0 ) {}
mousePressed ( MouseEvent a r g 0 ) {}
mouseReleased ( MouseEvent a r g 0 ) {}

38

JlniSquare.java
Listing 11: JlniSquare.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

package j l n i ;
import j a v a . awt . C o l o r ;
import j a v a . awt . Dimension ;
import j a v a . awt . G r a p h i c s ;
import j a v a . awt . Graphics2D ;
import j a v a . awt . T o o l k i t ;
import j a v a x . swing . JPanel ;
public c l a s s J l n i S q u a r e extends JPanel {
public C o l o r b g C o l o r = n u l l ;
public C o l o r p l C o l o r = n u l l ;
public i n t x ;
public i n t y ;
public i n t s q u a r e S t a t u s ;
public boolean validMove = f a l s e ;
public J l n i S q u a r e ( C o l o r background ,
i n t s t a t u s , boolean vMove , i n t xCoord , i n t yCoord ) {
validMove = vMove ;
b g C o l o r = background ;
squareStatus = status ;
x = xCoord ;
y = yCoord ;
i f ( s q u a r e S t a t u s == 1 ) {
// B l a c k
p l C o l o r = new C o l o r ( 0 , 0 , 0 ) ;
}
i f ( s q u a r e S t a t u s == 2 ) {
// White
p l C o l o r = new C o l o r ( 2 5 5 , 2 5 5 , 2 5 5 ) ;
}
}
public void paintComponent ( G r a p h i c s g ) {
Dimension dim = T o o l k i t . g e t D e f a u l t T o o l k i t ( ) . g e t S c r e e n S i z e ( ) ;
super . paintComponent ( g ) ;
Graphics2D g2 = ( Graphics2D ) g ;
g2 . s e t C o l o r ( b g C o l or ) ;
g2 . f i l l R e c t ( 1 , 1 , 5 0 0 , 5 0 0 ) ;
i f ( squareStatus > 0){
g2 . s e t C o l o r ( p l C o l o r ) ;
g2 . f i l l O v a l ( dim . h e i g h t / 7 5 ,
dim . h e i g h t / 7 5 ,
dim . h e i g h t / 3 5 ,
dim . h e i g h t / 3 5 ) ;
}
i f ( validMove == true ) {
g2 . s e t C o l o r (new C o l o r ( 1 7 6 , 3 1 , 3 4 ) ) ;
g2 . f i l l O v a l ( dim . h e i g h t / 4 5 , dim . h e i g h t / 4 5 , 1 0 , 1 0 ) ;
}

39

54
55
56

}
}

40

JlniComputerPlayer
Listing 12: JlniComputerPlayer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

/ O t h e l l o Computer P l a y e r /
/ C r e a t e d by Jon Lau N i e l s e n /
package j l n i ;
import j a v a . u t i l . A r r a y L i s t ;
import I n t e r f a c e s . ;
public c l a s s J l n i C o m p u t e r P l a y e r implements P l a y e r {
public J l n i B o a r d c u r r e n t B o a r d = new J l n i B o a r d ( ) ;
public f i n a l i n t maxDepth = 6 ;
public Coord b e s t P o s s i b l e M o v e ;
public boolean p l a y e r C o l o r ;
public S t r i n g playerName = Jons Bot ;
public void i n i t ( boolean c o l o r ) {
playerColor = color ;
}
public S t r i n g name ( ) {
return playerName ;
}
public void n o t i f y ( Coord move ) {
try {
c u r r e n t B o a r d . p l a y ( move , ! p l a y e r C o l o r ) ;
currentBoard . p r i n t S t a t e ( ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;
}
}
/@ S p e c i f i c a t i o n f o r method move
The method w i l l be c a l l e d by a r e f e r e e , t h r o w s
NoPossibleMovee x c e p t i o n , i f no moves can be
made e l s e i t l l c a l l myTurnmethod , and s t a r t
c a l c u l a t i n g which a b s o l u t e c o o r d i n a t e i s t h e
best .

@ i n p u t :
Nothing
@ o u t p u t : An a b s o l u t e c o o r d i n a t e , c a l c u l a t e d

by t h e minimaxa l g o r i t h m .
/
public Coord move ( ) throws NoPossibleMove {
b e s t P o s s i b l e M o v e=n u l l ;
i f ( c u r r e n t B o a r d . v a l i d M o v e s ( p l a y e r C o l o r ) . s i z e ( ) == 0 ) {
throw new NoPossibleMove ( ) ;
} else {
try {
myTurn ( c u r r e n t B o a r d , p l a y e r C o l o r , 0 , 1000000 , + 1 0 0 0 0 0 0 ) ;
currentBoard . play ( bestPossibleMove , playerColor ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;

41

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

}
}
currentBoard . p r i n t S t a t e ( ) ;
return b e s t P o s s i b l e M o v e ;
}
/@ S p e c i f i c a t i o n f o r method myTurn
Uses t h e minimax a l g o r i t h m . This i s t h e
maximizing p l a y e r .

@ i n p u t :
J l n i B o a r d , p l a y e r ( who s t u r n i t i s ) ,

c u r r e n t D e p t h ( which d e p t h we a r e on ) ,

a l p h a and b e t a
@ o u t p u t : Returns v a l u e o f t h e board ,

i f a t max depth , or t h e game i s over

e l s e i t returns s t r o n g e s t c h i l d ( alpha ) .
/
public i n t myTurn ( J l n i B o a r d board ,
boolean p l a y e r ,
int currentDepth ,
i n t alpha ,
int beta ) {
int value = 0 ;
i f ( board . gameOver == true | | c u r r e n t D e p t h == maxDepth ) {
return boardValue ( board ) ;
}
// Find v a l i d moves f o r t h e board
A r r a y L i s t <Coord> a r r V a l i d M o v e s = n u l l ;
a r r V a l i d M o v e s = board . v a l i d M o v e s ( p l a y e r ) ;
// For e v e r y v a l i d move
f o r ( i n t q =0; q<a r r V a l i d M o v e s . s i z e ( ) ; q++){
Coord move = a r r V a l i d M o v e s . g e t ( q ) ;
// Clone t h e boar d
J l n i B o a r d boardClone = new J l n i B o a r d ( ) ;
f o r ( i n t y =0; y<board . a r r S t a t e . l e n g t h ; y++){
f o r ( i n t x =0; x<board . a r r S t a t e [ y ] . l e n g t h ; x++){
boardClone . a r r S t a t e [ x ] [ y ] = board . a r r S t a t e [ x ] [ y ] ;
}
}
// Perform t h e move
try {
boardClone . p l a y ( move , p l a y e r ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;
}
// Take t h e v a l u e o f our c h i l d
v a l u e = h i sT urn ( boardClone , ! p l a y e r , c u r r e n t D e p t h +1 , beta , a l p h a ) ;
// P i c k i n g t h e b e s t c h i l d ( maximizing )
i f ( value > alpha ){
alpha = value ;
i f ( c u r r e n t D e p t h == 0 ) {
b e s t P o s s i b l e M o v e = move ;

42

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

}
}
// Pruning
i f ( a l p h a >= b e t a ) {
return a l p h a ;
}
}
// Return t h e b e s t c h i l d
return a l p h a ;
}
/@ S p e c i f i c a t i o n f o r method hisTurn
Uses t h e minimax a l g o r i t h m . This i s t h e
minimizing p l a y e r .

@ i n p u t :
J l n i B o a r d , p l a y e r ( who s t u r n i t i s ) ,

c u r r e n t D e p t h ( which d e p t h we a r e on ) ,

a l p h a and b e t a
@ o u t p u t : Returns v a l u e o f t h e board ,

i f a t max depth , or t h e game i s over

e l s e i t returns weakest c h i l d ( alpha ) .


/
public i n t h is Tu rn ( J l n i B o a r d board ,
boolean p l a y e r ,
int currentDepth ,
i n t alpha ,
int beta ) {
int value = 0 ;
i f ( board . gameOver == true | | c u r r e n t D e p t h == maxDepth ) {
return boardValue ( board ) ;
}
// Find v a l i d moves f o r t h e board
A r r a y L i s t <Coord> a r r V a l i d M o v e s = n u l l ;
a r r V a l i d M o v e s = board . v a l i d M o v e s ( p l a y e r ) ;
// For e v e r y v a l i d move
f o r ( i n t q =0; q<a r r V a l i d M o v e s . s i z e ( ) ; q++){
Coord move = a r r V a l i d M o v e s . g e t ( q ) ;
// C l o n i n g t h e boa rd
J l n i B o a r d boardClone = new J l n i B o a r d ( ) ;
f o r ( i n t y =0; y<board . a r r S t a t e . l e n g t h ; y++){
f o r ( i n t x =0; x<board . a r r S t a t e [ y ] . l e n g t h ; x++){
boardClone . a r r S t a t e [ x ] [ y ] = board . a r r S t a t e [ x ] [ y ] ;
}
}
// Perform t h e move
try {
boardClone . p l a y ( move , p l a y e r ) ;
} catch ( I l l e g a l M o v e e ) {
e . printStackTrace ( ) ;
}
// Take t h e v a l u e o f our c h i l d
v a l u e = myTurn ( boardClone , ! p l a y e r , c u r r e n t D e p t h +1 , beta , a l p h a ) ;

43

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

// Find t h e w e a k e s t c h i l d ( m i n i m i z i n g )
i f ( value < alpha ){
alpha = value ;
}
// Pruning
i f ( a l p h a <= b e t a ) {
return a l p h a ;
}
}
return a l p h a ;
}
/@ S p e c i f i c a t i o n f o r method bo ar dV al ue
E v a l u a t e s t h e b oard .

@ i n p u t :
J l n i B o a r d , which w i l l be

evaluated .
@ o u t p u t : An i n t , r e p r e s e n t i n g t h e v a l u e

o f t h e boar d .
/
private i n t boardValue ( J l n i B o a r d board ) {
int r e s u l t = 0 ;
i n t [ ] s c o r e = board . s c o r e ( ) ;
// Checking i f t h e game i s o v e r
i f ( board . gameOver == true ) {
// I f b l a c k i s t h e winner
if ( score [ 1 ] > score [0]){
// I f we a r e b l a c k
i f ( p l a y e r C o l o r == f a l s e ) {
return 1 0 0 0 0 0 ;
} else {
//We a r e w h i t e
return 100000;
}
}
// I f w h i t e i s t h e winner
if ( score [ 1 ] > score [0]){
// I f we a r e b l a c k
i f ( p l a y e r C o l o r == true ) {
return 100000;
} else {
//We a r e w h i t e
return 1 0 0 0 0 0 ;
}
}
}
// Scor e
i f ( p l a y e r C o l o r == f a l s e ) {
result = result + score [ 1 ] ;
} else {
result = result + score [ 0 ] ;
}
// M o b i l i t y
A r r a y L i s t <Coord> v a l i d M o v e s = board . v a l i d M o v e s ( p l a y e r C o l o r ) ;

44

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

int m o b i l i t y = validMoves . s i z e ( ) ;
result = result + ( mobility 3);
// F i e l d s n e x t t o c o r n e r s .
Coord arrBadTopLeft [ ] = { new Coord ( 1 , 0 ) , new Coord ( 1 , 1 ) ,
new Coord ( 0 , 1 ) } ;
Coord arrBadTopRight [ ] = { new Coord ( 6 , 0 ) , new Coord ( 6 , 1 ) ,
new Coord ( 7 , 1 ) } ;
Coord arrBadDownLeft [ ] = { new Coord ( 0 , 6 ) , new Coord ( 1 , 6 ) ,
new Coord ( 1 , 7 ) } ;
Coord arrBadDownRight [ ] = { new Coord ( 6 , 6 ) , new Coord ( 6 , 7 ) ,
new Coord ( 7 , 6 ) } ;
f o r ( i n t i =0; i <arrBadTopLeft . l e n g t h ; i ++){
i f ( board . a r r S t a t e [ 0 ] [ 0 ] == 0
&& board . a r r S t a t e [ arrBadTopLeft [ i ] . getX ( ) ] [ arrBadTopLeft [ i ] . getY ( ) ]
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result 10;
}
}
f o r ( i n t i =0; i <arrBadTopRight . l e n g t h ;
i f ( board . a r r S t a t e [ 7 ] [ 0 ] == 0
&& board . a r r S t a t e [ arrBadTopRight [ i
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result 10;
}
}
f o r ( i n t i =0; i <arrBadDownLeft . l e n g t h ;
i f ( board . a r r S t a t e [ 0 ] [ 7 ] == 0
&& board . a r r S t a t e [ arrBadDownLeft [ i
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result 10;
}
}
f o r ( i n t i =0; i <arrBadDownRight . l e n g t h
i f ( board . a r r S t a t e [ 7 ] [ 7 ] == 0
&& board . a r r S t a t e [ arrBadDownRight [
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result 10;
}
}

i ++){
] . getX ( ) ] [ arrBadTopRight [ i ] . getY ( ) ]

i ++){
] . getX ( ) ] [ arrBadDownLeft [ i ] . getY ( ) ]

; i ++){
i ] . getX ( ) ] [ arrBadDownRight [ i ] . getY ( ) ]

// Corners
Coord a r r C o r n e r s [ ] = { new Coord ( 0 , 0 ) , new Coord ( 7 , 7 ) ,
new Coord ( 7 , 0 ) , new Coord ( 0 , 7 ) } ;
f o r ( i n t i =0; i <a r r C o r n e r s . l e n g t h ; i ++){
i f ( board . a r r S t a t e [ a r r C o r n e r s [ i ] . getX ( ) ] [ a r r C o r n e r s [ i ] . getY ( ) ]
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result + 50;
}
}
// S i d e s
Coord a r r S i d e s [ ] = { new Coord ( 0 , 2 ) , new Coord ( 0 , 3 ) ,

45

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

new
new
new
new
new

Coord ( 0 , 4 ) ,
Coord ( 7 , 3 ) ,
Coord ( 2 , 0 ) ,
Coord ( 5 , 0 ) ,
Coord ( 4 , 7 ) ,

new
new
new
new
new

Coord ( 0 , 5 ) , new
Coord ( 7 , 4 ) , new
Coord ( 3 , 0 ) , new
Coord ( 2 , 7 ) , new
Coord ( 5 , 7 ) } ;

Coord ( 7 , 2 ) ,
Coord ( 7 , 5 ) ,
Coord ( 4 , 0 ) ,
Coord ( 3 , 7 ) ,

f o r ( i n t i =0; i <a r r S i d e s . l e n g t h ; i ++){


i f ( board . a r r S t a t e [ a r r S i d e s [ i ] . getX ( ) ] [ a r r S i d e s [ i ] . getY ( ) ]
== board . i n t P l a y e r ( p l a y e r C o l o r ) ) {
result = result + 5;
}
}
return r e s u l t ;
}
}

46

You might also like