Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 56fc29b

Browse files
committed
added tictactoe, unit tests, minimax
1 parent 7b617ef commit 56fc29b

File tree

7 files changed

+427
-0
lines changed

7 files changed

+427
-0
lines changed

‎CCSPiJ/src/chapter8/Board.java‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Board.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
import java.util.List;
20+
21+
public interface Board<Move> {
22+
Piece getTurn();
23+
24+
Board<Move> move(Move location);
25+
26+
List<Move> getLegalMoves();
27+
28+
boolean isWin();
29+
30+
default boolean isDraw() {
31+
return (!isWin() && (getLegalMoves().size() == 0));
32+
}
33+
34+
double evaluate(Piece player);
35+
}

‎CCSPiJ/src/chapter8/Minimax.java‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Minimax.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
public class Minimax {
20+
// Find the best possible outcome for originalPlayer
21+
public static <Move> double minimax(Board<Move> board, boolean maximizing, Piece originalPlayer, int maxDepth) {
22+
// Base case - terminal position or maximum depth reached
23+
if (board.isWin() || board.isDraw() || maxDepth == 0) {
24+
return board.evaluate(originalPlayer);
25+
}
26+
// Recursive case - maximize your gains or minimize the opponent's gains
27+
if (maximizing) {
28+
double bestEval = Double.NEGATIVE_INFINITY; // result must be higher
29+
for (Move move : board.getLegalMoves()) {
30+
double result = minimax(board.move(move), false, originalPlayer, maxDepth - 1);
31+
bestEval = Math.max(result, bestEval);
32+
}
33+
return bestEval;
34+
} else { // minimizing
35+
double worstEval = Double.POSITIVE_INFINITY; // result must be lower
36+
for (Move move : board.getLegalMoves()) {
37+
double result = minimax(board.move(move), true, originalPlayer, maxDepth - 1);
38+
worstEval = Math.min(result, worstEval);
39+
}
40+
return worstEval;
41+
}
42+
}
43+
44+
// Find the best possible move in the current position
45+
// looking up to maxDepth ahead
46+
public static <Move> Move findBestMove(Board<Move> board, int maxDepth) {
47+
double bestEval = Double.NEGATIVE_INFINITY;
48+
Move bestMove = null; // won't stay null for sure
49+
for (Move move : board.getLegalMoves()) {
50+
double result = minimax(board.move(move), false, board.getTurn(), maxDepth);
51+
if (result > bestEval) {
52+
bestEval = result;
53+
bestMove = move;
54+
}
55+
}
56+
return bestMove;
57+
}
58+
}

‎CCSPiJ/src/chapter8/Piece.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Piece.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
public interface Piece {
20+
Piece opposite();
21+
}

‎CCSPiJ/src/chapter8/TTTBoard.java‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// TTTBoard.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
23+
public class TTTBoard implements Board<Integer> {
24+
private static final int NUM_SQUARES = 9;
25+
private TTTPiece[] position;
26+
private TTTPiece turn;
27+
28+
public TTTBoard(TTTPiece[] position, TTTPiece turn) {
29+
this.position = position;
30+
this.turn = turn;
31+
}
32+
33+
public TTTBoard() {
34+
// by default start with blank board
35+
position = new TTTPiece[NUM_SQUARES];
36+
Arrays.fill(position, TTTPiece.E);
37+
// X goes first
38+
turn = TTTPiece.X;
39+
}
40+
41+
@Override
42+
public Piece getTurn() {
43+
return turn;
44+
}
45+
46+
@Override
47+
public TTTBoard move(Integer location) {
48+
TTTPiece[] tempPosition = Arrays.copyOf(position, position.length);
49+
tempPosition[location] = turn;
50+
return new TTTBoard(tempPosition, turn.opposite());
51+
}
52+
53+
@Override
54+
public List<Integer> getLegalMoves() {
55+
ArrayList<Integer> legalMoves = new ArrayList<>();
56+
for (int i = 0; i < NUM_SQUARES; i++) {
57+
// empty squares are legal moves
58+
if (position[i] == TTTPiece.E) {
59+
legalMoves.add(i);
60+
}
61+
}
62+
return legalMoves;
63+
}
64+
65+
@Override
66+
public boolean isWin() {
67+
// three row, three column, and then two diagonal checks
68+
return position[0] == position[1] && position[0] == position[2] && position[0] != TTTPiece.E ||
69+
position[3] == position[4] && position[3] == position[5] && position[3] != TTTPiece.E ||
70+
position[6] == position[7] && position[6] == position[8] && position[6] != TTTPiece.E ||
71+
position[0] == position[3] && position[0] == position[6] && position[0] != TTTPiece.E ||
72+
position[1] == position[4] && position[1] == position[7] && position[1] != TTTPiece.E ||
73+
position[2] == position[5] && position[2] == position[8] && position[2] != TTTPiece.E ||
74+
position[0] == position[4] && position[0] == position[8] && position[0] != TTTPiece.E ||
75+
position[2] == position[4] && position[2] == position[6] && position[2] != TTTPiece.E;
76+
}
77+
78+
@Override
79+
public double evaluate(Piece player) {
80+
if (isWin() && turn == player) {
81+
return -1;
82+
} else if (isWin() && turn != player) {
83+
return 1;
84+
} else {
85+
return 0.0;
86+
}
87+
}
88+
89+
@Override
90+
public String toString() {
91+
StringBuilder sb = new StringBuilder();
92+
for (int row = 0; row < 3; row++) {
93+
for (int col = 0; col < 3; col++) {
94+
sb.append(position[row * 3 + col].toString());
95+
if (col != 2) {
96+
sb.append("|");
97+
}
98+
}
99+
sb.append(System.lineSeparator());
100+
if (row != 2) {
101+
sb.append("-----");
102+
sb.append(System.lineSeparator());
103+
}
104+
}
105+
return sb.toString();
106+
}
107+
108+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// TTTMinimaxTests.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.reflect.Method;
22+
23+
// Annotation for unit tests
24+
@Retention(RetentionPolicy.RUNTIME)
25+
@interface UnitTest {
26+
String name() default "";
27+
}
28+
29+
public class TTTMinimaxTests {
30+
31+
// Check if two values are equal and report back
32+
public static <T> void assertEquality(T actual, T expected) {
33+
if (actual.equals(expected)) {
34+
System.out.println("Passed!");
35+
} else {
36+
System.out.println("Failed!");
37+
System.out.println("Actual: " + actual.toString());
38+
System.out.println("Expected: " + expected.toString());
39+
}
40+
}
41+
42+
@UnitTest(name = "Easy Position")
43+
public void easyPosition() {
44+
TTTPiece[] toWinEasyPosition = new TTTPiece[] {
45+
TTTPiece.X, TTTPiece.O, TTTPiece.X,
46+
TTTPiece.X, TTTPiece.E, TTTPiece.O,
47+
TTTPiece.E, TTTPiece.E, TTTPiece.O };
48+
TTTBoard testBoard1 = new TTTBoard(toWinEasyPosition, TTTPiece.X);
49+
Integer answer1 = Minimax.findBestMove(testBoard1, 8);
50+
assertEquality(answer1, 6);
51+
}
52+
53+
@UnitTest(name = "Block Position")
54+
public void blockPosition() {
55+
TTTPiece[] toBlockPosition = new TTTPiece[] {
56+
TTTPiece.X, TTTPiece.E, TTTPiece.E,
57+
TTTPiece.E, TTTPiece.E, TTTPiece.O,
58+
TTTPiece.E, TTTPiece.X, TTTPiece.O };
59+
TTTBoard testBoard2 = new TTTBoard(toBlockPosition, TTTPiece.X);
60+
Integer answer2 = Minimax.findBestMove(testBoard2, 8);
61+
assertEquality(answer2, 2);
62+
}
63+
64+
@UnitTest(name = "Hard Position")
65+
public void hardPosition() {
66+
TTTPiece[] toWinHardPosition = new TTTPiece[] {
67+
TTTPiece.X, TTTPiece.E, TTTPiece.E,
68+
TTTPiece.E, TTTPiece.E, TTTPiece.O,
69+
TTTPiece.O, TTTPiece.X, TTTPiece.E };
70+
TTTBoard testBoard3 = new TTTBoard(toWinHardPosition, TTTPiece.X);
71+
Integer answer3 = Minimax.findBestMove(testBoard3, 8);
72+
assertEquality(answer3, 1);
73+
}
74+
75+
// Run all methods marked with the UnitTest annotation
76+
public void runAllTests() {
77+
for (Method method : this.getClass().getMethods()) {
78+
for (UnitTest annotation : method.getAnnotationsByType(UnitTest.class)) {
79+
System.out.println("Running Test " + annotation.name());
80+
try {
81+
method.invoke(this);
82+
} catch (Exception e) {
83+
e.printStackTrace();
84+
}
85+
System.out.println("____________________");
86+
}
87+
}
88+
}
89+
90+
public static void main(String[] args) {
91+
new TTTMinimaxTests().runAllTests();
92+
}
93+
}

‎CCSPiJ/src/chapter8/TTTPiece.java‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// TTTPiece.java
2+
// From Classic Computer Science Problems in Java Chapter 8
3+
// Copyright 2020 David Kopec
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package chapter8;
18+
19+
public enum TTTPiece implements Piece {
20+
X, O, E; // E is Empty
21+
22+
@Override
23+
public TTTPiece opposite() {
24+
if (this == TTTPiece.X) {
25+
return TTTPiece.O;
26+
} else if (this == TTTPiece.O) {
27+
return TTTPiece.X;
28+
} else {
29+
return TTTPiece.E;
30+
}
31+
}
32+
33+
@Override
34+
public String toString() {
35+
switch (this) {
36+
case X:
37+
return "X";
38+
case O:
39+
return "O";
40+
default: // E, empty
41+
return " ";
42+
}
43+
44+
}
45+
46+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /