mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 19:04:49 +00:00
Moves flip dots. all tests pass. can play reversi local.
This commit is contained in:
@@ -104,7 +104,6 @@ public abstract class GameCanvas {
|
|||||||
|
|
||||||
graphics.setFill(color);
|
graphics.setFill(color);
|
||||||
graphics.fillOval(x, y, width, height);
|
graphics.fillOval(x, y, width, height);
|
||||||
IO.println("gothere");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resize(int width, int height) {
|
public void resize(int width, int height) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.toop.game.Game;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class ReversiCanvas extends GameCanvas{
|
public class ReversiCanvas extends GameCanvas{
|
||||||
private Game.Move[] mostRecentLegalMoves;
|
|
||||||
public ReversiCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
public ReversiCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
||||||
super(color, width, height, 8, 8, 10, true, onCellClicked);
|
super(color, width, height, 8, 8, 10, true, onCellClicked);
|
||||||
drawStartingDots();
|
drawStartingDots();
|
||||||
@@ -20,18 +19,8 @@ public class ReversiCanvas extends GameCanvas{
|
|||||||
drawDot(Color.WHITE,27);
|
drawDot(Color.WHITE,27);
|
||||||
}
|
}
|
||||||
public void drawLegalMoves(Game.Move[] moves){
|
public void drawLegalMoves(Game.Move[] moves){
|
||||||
mostRecentLegalMoves = moves;
|
|
||||||
for(Game.Move move : moves){
|
for(Game.Move move : moves){
|
||||||
IO.println("Legal Moves:" + move.position());
|
|
||||||
drawDot(new Color(1f,0,0,0.25f),move.position());
|
drawDot(new Color(1f,0,0,0.25f),move.position());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void removeLegalMoves(){
|
|
||||||
if (mostRecentLegalMoves != null){
|
|
||||||
for(Game.Move move : mostRecentLegalMoves){
|
|
||||||
drawDot(Color.GRAY,move.position()); //todo get current background color or make this redraw the entire board
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mostRecentLegalMoves = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.toop.app.layer.*;
|
|||||||
import org.toop.app.layer.containers.HorizontalContainer;
|
import org.toop.app.layer.containers.HorizontalContainer;
|
||||||
import org.toop.app.layer.containers.VerticalContainer;
|
import org.toop.app.layer.containers.VerticalContainer;
|
||||||
import org.toop.app.layer.layers.MainLayer;
|
import org.toop.app.layer.layers.MainLayer;
|
||||||
|
import org.toop.game.Game;
|
||||||
import org.toop.game.reversi.Reversi;
|
import org.toop.game.reversi.Reversi;
|
||||||
import org.toop.game.reversi.ReversiAI;
|
import org.toop.game.reversi.ReversiAI;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
@@ -19,7 +20,9 @@ public class ReversiLayer extends Layer{
|
|||||||
super("bg-secondary"); //make reversiboard background dark green
|
super("bg-secondary"); //make reversiboard background dark green
|
||||||
|
|
||||||
canvas = new ReversiCanvas(Color.GREEN,(App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
|
canvas = new ReversiCanvas(Color.GREEN,(App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
|
||||||
IO.println("clicked reversi cell: "+cell);
|
reversi.play(new Game.Move(cell,reversi.getCurrentPlayer()));
|
||||||
|
reload();
|
||||||
|
canvas.drawLegalMoves(reversi.getLegalMoves());
|
||||||
});
|
});
|
||||||
reversi = new Reversi() ;
|
reversi = new Reversi() ;
|
||||||
reversiAI = new ReversiAI();
|
reversiAI = new ReversiAI();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.toop.game.tictactoe.TicTacToe;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ import java.util.Set;
|
|||||||
public final class Reversi extends TurnBasedGame {
|
public final class Reversi extends TurnBasedGame {
|
||||||
private int movesTaken;
|
private int movesTaken;
|
||||||
public static final char FIRST_MOVE = 'B';
|
public static final char FIRST_MOVE = 'B';
|
||||||
private Set<Point> filledCells = new HashSet<Point>();
|
private Set<Point> filledCells = new HashSet<>();
|
||||||
|
|
||||||
public Reversi() {
|
public Reversi() {
|
||||||
super(8, 8, 2);
|
super(8, 8, 2);
|
||||||
@@ -48,8 +49,10 @@ public final class Reversi extends TurnBasedGame {
|
|||||||
char[][] boardGrid = makeBoardAGrid();
|
char[][] boardGrid = makeBoardAGrid();
|
||||||
char currentPlayer = (currentTurn==0) ? 'B' : 'W';
|
char currentPlayer = (currentTurn==0) ? 'B' : 'W';
|
||||||
Set<Point> adjCell = getAdjacentCells(boardGrid);
|
Set<Point> adjCell = getAdjacentCells(boardGrid);
|
||||||
|
|
||||||
for (Point point : adjCell){
|
for (Point point : adjCell){
|
||||||
int score = getScoreForPotentialMove(point,boardGrid,currentPlayer);
|
Move[] moves = getFlipsForPotentialMove(point,boardGrid,currentPlayer);
|
||||||
|
int score = moves.length;
|
||||||
if (score > 0){
|
if (score > 0){
|
||||||
legalMoves.add(new Move(point.x + point.y * 8, currentPlayer));
|
legalMoves.add(new Move(point.x + point.y * 8, currentPlayer));
|
||||||
}
|
}
|
||||||
@@ -63,11 +66,11 @@ public final class Reversi extends TurnBasedGame {
|
|||||||
for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++){ //check adjacent cells
|
for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++){ //check adjacent cells
|
||||||
for (int deltaRow = -1; deltaRow <= 1; deltaRow++){ //orthogonally and diagonally
|
for (int deltaRow = -1; deltaRow <= 1; deltaRow++){ //orthogonally and diagonally
|
||||||
int newX = point.x + deltaColumn, newY = point.y + deltaRow;
|
int newX = point.x + deltaColumn, newY = point.y + deltaRow;
|
||||||
if (deltaColumn == 0 || deltaRow == 0 //continue if out of bounds
|
if (deltaColumn == 0 && deltaRow == 0 //continue if out of bounds
|
||||||
|| !isOnBoard(newX, newY)) {
|
|| !isOnBoard(newX, newY)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (boardGrid[newX][newY] == Game.EMPTY) { //check if the cell is empty
|
if (boardGrid[newY][newX] == Game.EMPTY) { //check if the cell is empty
|
||||||
possibleCells.add(new Point(newX, newY)); //and then add it to the set of possible moves
|
possibleCells.add(new Point(newX, newY)); //and then add it to the set of possible moves
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,66 +79,75 @@ public final class Reversi extends TurnBasedGame {
|
|||||||
return possibleCells;
|
return possibleCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getScoreForPotentialMove(Point point, char[][] boardGrid, char currentPlayer) {
|
public Move[] getFlipsForPotentialMove(Point point, char[][] boardGrid, char currentPlayer) {
|
||||||
int score = 0;
|
final ArrayList<Move> movesToFlip = new ArrayList<>();
|
||||||
for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++) {
|
for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++) {
|
||||||
for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
|
for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
|
||||||
if (deltaColumn == 0 && deltaRow == 0){
|
if (deltaColumn == 0 && deltaRow == 0){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
score += getScoreInDirection(point,boardGrid,currentPlayer,deltaColumn,deltaRow);
|
Move[] moves = getFlipsInDirection(point,boardGrid,currentPlayer,deltaColumn,deltaRow);
|
||||||
|
if (moves != null) {
|
||||||
|
movesToFlip.addAll(Arrays.asList(moves));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return score;
|
}
|
||||||
|
return movesToFlip.toArray(new Move[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getScoreInDirection(Point point, char[][] boardGrid, char currentPlayer, int dirX, int dirY) {
|
private Move[] getFlipsInDirection(Point point, char[][] boardGrid, char currentPlayer, int dirX, int dirY) {
|
||||||
char opponent = getOpponent(currentPlayer);
|
char opponent = getOpponent(currentPlayer);
|
||||||
|
final ArrayList<Move> movesToFlip = new ArrayList<>();
|
||||||
int x = point.x + dirX;
|
int x = point.x + dirX;
|
||||||
int y = point.y + dirY;
|
int y = point.y + dirY;
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (!isOnBoard(x, y) || boardGrid[y][x] != opponent) {
|
if (!isOnBoard(x, y) || boardGrid[y][x] != opponent) {
|
||||||
return 0;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (isOnBoard(x, y) && boardGrid[y][x] == opponent) {
|
while (isOnBoard(x, y) && boardGrid[y][x] == opponent) {
|
||||||
count++;
|
|
||||||
|
movesToFlip.add(new Move(x+y*8, currentPlayer));
|
||||||
x += dirX;
|
x += dirX;
|
||||||
y += dirY;
|
y += dirY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOnBoard(x, y) && boardGrid[y][x] == currentPlayer) {
|
if (isOnBoard(x, y) && boardGrid[y][x] == currentPlayer) {
|
||||||
return count;
|
return movesToFlip.toArray(new Move[0]);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOnBoard(int x, int y) {
|
private boolean isOnBoard(int x, int y) {
|
||||||
return x >= 0 && x < 8 && y >= 0 && y < 8;
|
return x >= 0 && x < 8 && y >= 0 && y < 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private char getOpponent(char currentPlayer){
|
|
||||||
if (currentPlayer == 'B') {
|
|
||||||
return 'W';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 'B';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public char[][] makeBoardAGrid() {
|
public char[][] makeBoardAGrid() {
|
||||||
char[][] boardGrid = new char[8][8];
|
char[][] boardGrid = new char[8][8];
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
boardGrid[i / 8][i % 8] = board[i];
|
boardGrid[i / 8][i % 8] = board[i]; //boardGrid[y / row] [x / column]
|
||||||
}
|
}
|
||||||
return boardGrid;
|
return boardGrid;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public State play(Move move) {
|
public State play(Move move) {
|
||||||
filledCells.add(new Point(move.position() / 8, move.position() % 8));
|
Move[] legalMoves = getLegalMoves();
|
||||||
|
boolean moveIsLegal = false;
|
||||||
|
for (Move legalMove : legalMoves) {
|
||||||
|
if (move.equals(legalMove)) {
|
||||||
|
moveIsLegal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (moveIsLegal) {
|
||||||
|
Move[] moves = getFlipsForPotentialMove(new Point(move.position()%8,move.position()/8), makeBoardAGrid(), move.value());
|
||||||
|
board[move.position()] = move.value();
|
||||||
|
for (Move m : moves) {
|
||||||
|
board[m.position()] = m.value();
|
||||||
|
}
|
||||||
|
updateFilledCellsSet();
|
||||||
nextTurn();
|
nextTurn();
|
||||||
|
return State.NORMAL;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +164,15 @@ public final class Reversi extends TurnBasedGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private char getOpponent(char currentPlayer){
|
||||||
|
if (currentPlayer == 'B') {
|
||||||
|
return 'W';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Game.Score getScore(){
|
public Game.Score getScore(){
|
||||||
int player1Score = 0, player2Score = 0;
|
int player1Score = 0, player2Score = 0;
|
||||||
for (int count = 0; count < 63; count++) {
|
for (int count = 0; count < 63; count++) {
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import org.toop.game.Game;
|
|||||||
public final class ReversiAI extends AI<Reversi> {
|
public final class ReversiAI extends AI<Reversi> {
|
||||||
@Override
|
@Override
|
||||||
public Game.Move findBestMove(Reversi game, int depth) {
|
public Game.Move findBestMove(Reversi game, int depth) {
|
||||||
return null;
|
return game.getLegalMoves()[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.toop.game.tictactoe;
|
|||||||
|
|
||||||
import org.toop.game.Game;
|
import org.toop.game.Game;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -30,46 +30,88 @@ class ReversiTest {
|
|||||||
assertEquals('B',game.board[35]);
|
assertEquals('B',game.board[35]);
|
||||||
assertEquals('W',game.board[36]);
|
assertEquals('W',game.board[36]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetLegalMovesAtStart() {
|
void testGetLegalMovesAtStart() {
|
||||||
Game.Move[] moves = game.getLegalMoves();
|
Game.Move[] moves = game.getLegalMoves();
|
||||||
|
List<Game.Move> expectedMoves = List.of(
|
||||||
|
new Game.Move(19,'B'),
|
||||||
|
new Game.Move(26,'B'),
|
||||||
|
new Game.Move(37,'B'),
|
||||||
|
new Game.Move(44,'B')
|
||||||
|
);
|
||||||
assertNotNull(moves);
|
assertNotNull(moves);
|
||||||
assertTrue(moves.length > 0);
|
assertTrue(moves.length > 0);
|
||||||
assertEquals(new Game.Move(19,'B'),moves[0]);
|
assertMovesMatchIgnoreOrder(expectedMoves, Arrays.asList(moves));
|
||||||
assertEquals(new Game.Move(26,'B'),moves[0]);
|
|
||||||
assertEquals(new Game.Move(37,'B'),moves[0]);
|
|
||||||
assertEquals(new Game.Move(44,'B'),moves[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertMovesMatchIgnoreOrder(List<Game.Move> expected, List<Game.Move> actual) {
|
||||||
|
assertEquals(expected.size(), actual.size());
|
||||||
|
for (int i = 0; i < expected.size(); i++) {
|
||||||
|
assertTrue(actual.contains(expected.get(i)));
|
||||||
|
assertTrue(expected.contains(actual.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMakeValidMoveFlipsPieces() {
|
void testMakeValidMoveFlipsPieces() {
|
||||||
game.play(new Game.Move(19, 'B'));
|
game.play(new Game.Move(19, 'B'));
|
||||||
assertEquals('B', game.board[19]);
|
assertEquals('B', game.board[19]);
|
||||||
assertEquals('B', game.board[27], "Piece should have flipped to B");
|
assertEquals('B', game.board[27], "Piece should have flipped to B");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMakeInvalidMoveDoesNothing() {
|
void testMakeInvalidMoveDoesNothing() {
|
||||||
char[] before = game.board.clone();
|
char[] before = game.board.clone();
|
||||||
game.play(new Game.Move(0, 'B'));
|
game.play(new Game.Move(0, 'B'));
|
||||||
assertArrayEquals(before, game.board, "Board should not change on invalid move");
|
assertArrayEquals(before, game.board, "Board should not change on invalid move");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testTurnSwitchesAfterValidMove() {
|
void testTurnSwitchesAfterValidMove() {
|
||||||
char current = game.getCurrentPlayer();
|
char current = game.getCurrentPlayer();
|
||||||
game.play(game.getLegalMoves()[0]);
|
game.play(game.getLegalMoves()[0]);
|
||||||
assertNotEquals(current, game.getCurrentPlayer(), "Player turn should switch after a valid move");
|
assertNotEquals(current, game.getCurrentPlayer(), "Player turn should switch after a valid move");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCountScoreCorrectly() {
|
void testCountScoreCorrectlyAtStart() {
|
||||||
|
long start = System.nanoTime();
|
||||||
Game.Score score = game.getScore();
|
Game.Score score = game.getScore();
|
||||||
assertEquals(2, score.player1Score()); // Black
|
assertEquals(2, score.player1Score()); // Black
|
||||||
assertEquals(2, score.player2Score()); // White
|
assertEquals(2, score.player2Score()); // White
|
||||||
|
long end = System.nanoTime();
|
||||||
|
IO.println((end-start));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCountScoreCorrectlyAtEnd() {
|
||||||
|
for (int i = 0; i < 10; i++){
|
||||||
|
game = new Reversi();
|
||||||
|
Game.Move[] legalMoves = game.getLegalMoves();
|
||||||
|
while(legalMoves.length > 0) {
|
||||||
|
game.play(legalMoves[(int)(Math.random()*legalMoves.length)]);
|
||||||
|
legalMoves = game.getLegalMoves();
|
||||||
|
}
|
||||||
|
Game.Score score = game.getScore();
|
||||||
|
IO.println(score.player1Score());
|
||||||
|
IO.println(score.player2Score());
|
||||||
|
char[][] grid = game.makeBoardAGrid();
|
||||||
|
for (char[] chars : grid) {
|
||||||
|
IO.println(Arrays.toString(chars));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testAISelectsLegalMove() {
|
void testAISelectsLegalMove() {
|
||||||
Game.Move move = ai.findBestMove(game,4);
|
Game.Move move = ai.findBestMove(game,4);
|
||||||
assertNotNull(move);
|
assertNotNull(move);
|
||||||
assertTrue(containsMove(game.getLegalMoves(),move), "AI should always choose a legal move");
|
assertTrue(containsMove(game.getLegalMoves(),move), "AI should always choose a legal move");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsMove(Game.Move[] moves, Game.Move move) {
|
private boolean containsMove(Game.Move[] moves, Game.Move move) {
|
||||||
for (Game.Move m : moves) {
|
for (Game.Move m : moves) {
|
||||||
if (m.equals(move)) return true;
|
if (m.equals(move)) return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user