I'm working on a tictactoe board for practice making classes and i have ran into a problem with my algorithm. it seems to be returning the best move offensive, but it doesn't play defense. i dont know where i have messed up and cant seem to find it. i have looked over a lot of things on here about it and ive compared it to simular projects, but still can't seem to get it. here is my code.
package TicTacToe;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
public class Solution {
private static GameBoard currentBoard;
private static Player botPlayer;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String player;
System.out.println("ENTER bot: ");
player = in.next();
if(player.equalsIgnoreCase("X")) {
botPlayer = Player.X;}
else {botPlayer = Player.O;}
String board[] = new String[3];
for(int i = 0; i < 3; i++) {
System.out.println("ENTER board: ");
board[i] = in.next();
}
currentBoard = new GameBoard(3,3, board);
List<Space> OpenSpaces = getOpenSquares(currentBoard);
MakeMove(OpenSpaces);
System.exit(-1);
}
public static List<Space> getOpenSquares(GameBoard GB) {
List<Space> OpenSpaces = new ArrayList<Space>();
for(int r = 0; r < 3; r++) {
for(int c = 0; c < 3; c++) {
if(GB.squares[r][c] == Player.Open) {
OpenSpaces.add(new Space(r,c));
}
}
}
return OpenSpaces;
}
private static Space bestMove;
private static Space currentMove;
private static Space previousMove;
private static void MakeMove(List<Space> OpenSpaces) {
if(OpenSpaces.size() == currentBoard.Size) {
Random random = new Random();
bestMove = new Space(random.nextInt(2),2);
} else {
for(Space child: OpenSpaces) {
currentMove = GetBestMove(currentBoard,botPlayer);
if (currentMove != null){
}else{
continue;}
if(previousMove != null && previousMove.Rank < currentMove.Rank ||
previousMove == null && currentMove != null) {
bestMove = currentMove;
}
previousMove = currentMove;
}
}
if (bestMove != null) {
currentBoard.squares[bestMove.X][bestMove.Y] = botPlayer;
System.out.println("the best move is: " + currentMove.X + " " + currentMove.Y);
}
}
private static Space GetBestMove(GameBoard gb, Player p) {
Space bestSpace = null;
List<Space> cloneOpenSpaces = getOpenSquares(gb);
GameBoard cloneBoard = null;
cloneBoard = gb.Clone();
for(Space Open: cloneOpenSpaces) {
cloneBoard = gb.Clone();
Space newSpace = Open;
cloneBoard.squares[newSpace.X][newSpace.Y] = p;
if(cloneBoard.Winner == Player.Open && cloneOpenSpaces.size() > 0) {
Player InP;
if(p == Player.X) {
InP = Player.O;
}else {
InP = Player.X;
}
Space tempMove = GetBestMove(cloneBoard, InP);
if(tempMove != null){
newSpace.Rank = tempMove.Rank;
}
} else {
if(cloneBoard.Winner == Player.Open) {
newSpace.Rank = 0;
}else if(cloneBoard.Winner == Player.O) {
newSpace.Rank = -1;
}else if(cloneBoard.Winner == Player.X) {
newSpace.Rank = 1;
}
}
System.out.println(newSpace.Rank);
if(bestSpace == null ||
(p == Player.X && newSpace.Rank < ((Space)bestSpace).Rank)||
(p == Player.O && newSpace.Rank > ((Space)bestSpace).Rank)) {
bestSpace = newSpace;
}
}
return (Space)bestSpace;
}
public static enum Player {
X (1),
O (-1),
Open (0);
private final double value;
Player(double value){
this.value = value;
}
}
public static class Space {
public int X;
public int Y;
public double Rank;
public Space(int x, int y) {
this.X = x;
this.Y = y;
Rank = 0;
}
public Space() {
}
}
public static class GameBoard {
public int Rows;
public int getRows() {
return this.Rows;
}
public void setRows(int rows) {
Rows = rows;
}
public int Columns;
public int getColumns() {
return this.Columns;
}
public void setColumns(int columns) {
Columns = columns;
}
public Player[][] squares;
//public Player[x][y]
public Player getPlayer(int x, int y) {
return this.squares[x][y];
}
public void setPlayer(int x, int y, Player player) {
squares[x][y] = player;
}
public boolean Full;
public boolean isFull() {
for(int r = 0; r < 2; r++) {
for(int c = 0; c < 2; c++) {
if (squares[r][c] != Player.Open) {return false;}
}
}
return true;
}
public int Size;
public int getSize() {
return this.Size;
}
public void setSize(int size) {
Size = size;
}
public List<Space> OpenSquares;
public List<Space> getOpenSquares() {
List<Space> OpenSquares = new ArrayList<Space>();
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
if(squares[r][c] == Player.Open) {
OpenSquares.add(new Space(r,c));
}
}
}
return this.OpenSquares;
}
public Player Winner;
public Player getWinner() {
int count = 0;
//columns
for (int x = 0; x < Rows; x++)
{
count = 0;
for (int y = 0; y < Columns; y++) {
count += squares[x][y].value;
}
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
}
//rows
for (int x = 0; x < Rows; x++) {
count = 0;
for (int y = 0; y < Columns; y++) {
count += squares[y][x].value;
}
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
}
// Diagonals right to left
count = 0;
count += squares[0][0].value;
count += squares[1][1].value;
count += squares[2][2].value;
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
// Diagonals left to right
count = 0;
count += squares[0][2].value;
count += squares[1][1].value;
count += squares[2][0].value;
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
return Player.Open;
}
public GameBoard Clone() {
GameBoard b = new GameBoard(Rows,Columns);
b.squares = (Player[][])this.squares.clone();
b.Winner = getWinner();
return b;
}
// Class initializer
public GameBoard(int boardRows, int boardColumns, String[] board) {
// Set the dimensions
Rows = boardRows;
Columns = boardColumns;
// Create game spaces
squares = new Player[Rows][Columns];
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
//squares[i][n] = Player.Open;
if(board[r].charAt(c) == 'X') {
squares[r][c] = Player.X;
}
if(board[r].charAt(c) == 'O') {
squares[r][c] = Player.O;
}
if(board[r].charAt(c) == '_') {
squares[r][c] = Player.Open;
}
}
}
this.Winner = getWinner();
this.OpenSquares = getOpenSquares();
//Size of the board
this.Size = Rows * Columns;
}
// clone Class initializer
public GameBoard(int boardRows, int boardColumns) {
// Set the dimensions
Rows = boardRows;
Columns = boardColumns;
// Create game spaces
squares = new Player[Rows][Columns];
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
squares[r][c] = Player.Open;
}
}
this.Winner = getWinner();
this.OpenSquares = getOpenSquares();
//Size of the board
Size = Rows * Columns;
}
}
}
all of the classes are at the bottom. Thanks in advance for any help and corrections. :)
i made it recursive in the following code, although i still cant figure out the scoring.. if the value is either 1, 0, or -1 then if there are multipule moves with the same value it will just take the 1st one which may not be the best move "blocking.
private static Space GetBestMove(GameBoard gb, Player p) {
Space bestSpace = null;
List<Space> cloneOpenSpaces = getOpenSquares(gb);
GameBoard cloneBoard = null;
cloneBoard = gb.Clone();
for(Space Open: cloneOpenSpaces) {
cloneBoard = gb.Clone();
Space newSpace = Open;
cloneBoard.squares[newSpace.X][newSpace.Y] = p;
if(cloneBoard.Winner == Player.Open && cloneOpenSpaces.size() > 0) {
Player InP;
if(p == Player.X) {
InP = Player.O;
}else {
InP = Player.X;
}
***Space tempMove = GetBestMove(cloneBoard, InP);***
if(tempMove != null){
newSpace.Rank = tempMove.Rank;
}
the results of the test are as follows
test 1
ENTER bot:
O
ENTER board:
[ ][O][ ]
ENTER board:
[ ][ ][ ]
ENTER board:
[ ][X][X]
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
the best move is: 0 2
test 2
ENTER bot:
O
ENTER board:
[ ][X][X]
ENTER board:
[ ][ ][ ]
ENTER board:
[ ][O][ ]
1.0
1.0
1.0
1.0
1.0
-1.0
1.0
-1.0
-1.0
1.0
-1.0
1.0
1.0
-1.0
-1.0
the best move is: 1 1
I haven't ran your code, but I think I may know why you are having issues. The minimax algorithm is recursive in nature. You look at each open space, and determine some sort of score for each one. I see this in your code. However, what I don't see is the recursion that equates to the logic "if I move here, then what options will my opponent have during his next turn". Notice that you can keep calling the same scoring function, but scoring both players' options. This is where the computation can get intensive, and where stuff like pruning comes into play. Say I want to look 3 moves ahead. Say there are initially 5 open spaces. For each of the 5 open spaces, I examine my options and give a score to each one. Then I pretend to move there, and send the new board through the scoring function, and assume my opponent will take the highest scoring move of the remaining 4 possible moves. Then I pretend he moves there, and I again run the board through the scoring function, now with 2 hypothetical moves on it. You continue this for a set "depth", or number of potential moves, and pick the move that results in the highest value, assuming the opponent will do what you calculated they would.
I realize this was long-winded, but I hope there was a little bit of value buried in there somewhere. Take a look at your code, figure out where you are scoring moves (if you see a win, take it; if you can block a win, take it; etc.). Then continue calling this function where you keep adding fake/potential moves (those with the highest value from your scoring function), and once you reach the depth, you can simply pick the move that is likely to give you the most valuable outcome.
Basically, in your code, you should call GetBestMove(...) once from MakeMove(...). However, GetBestMove(...) should repeatedly call itself, with a modified board each time; and each time, it will return the best move given a hypothetical (or real) board. What I don't see in your code is that recursive call to GetBestMove(...), and the necessary upkeep that goes along with it. This explains why you only get aggressive behavior; it only looks to see what the best immediate move is, without any regard to what your opponent might be able to do if you make that move!
If my assumptions are wrong, provide a test case where you expect some behavior, but are getting something different.
Related
I am coding a simple tic-tac-toe for a high-school mini project, but I need it to be within a strict data volume (not more than 112 lines). I thought checking for each row, column and cross would be long, so is there any alternative to do so (You should see a [[[HERE]]] comment)? (Btw, I already know it looks awful) Thanks in advance!
public class TTTGame {
//OPTIONS v
public static final String draw = "DRAW"; // <- Definitions for different states
public static final String circles = "CIRCLES"; // BOT
public static final String crosses = "CROSSES"; // PLAYER
public static final String getCrosses = "X"; //<- Symbols to display
public static final String getCircles = "O";
//OPTIONS ^
//DO NOT MODIFY UNDER THIS LINE (Just kidding, do whatever u want) v
public static int[][] board = {
{0,0,0},
{0,0,0},
{0,0,0},
};
public static final int empty = 0; // Definition of the values
public static final int cross = 1;
public static final int circle = 2;
public static int turns = 0; //Just here to count turns, nothing special
public static void main(String[]args) { //Main process
board[1][1] = circle;
display();
while (true) {
PlayerTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
BotTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
display();
turns += 1;
}
}
private static void GStop() { //Force stop the match function
System.out.println("Winner : " + checkWinner());
System.exit(1);
}
private static boolean checkStop() { //Check if match is already full / completed (Draw)
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
if (board[x][y]==empty) return false;
return true;
}
#Nullable
private static String checkWinner() { //Check Winner
// [[[ HERE ]]] ---------------
return null;
}
private static void PlayerTurn() { //Player turn
int x; Scanner c = new Scanner(System.in);
while (true) {
x = c.nextInt();
x = x-1;
if ((x>=0)&&(x < 9)) {
if (board[x / 3][x % 3] == empty) {
board[x / 3][x % 3] = cross;
break;
} else System.out.println("Already chosen");
} else System.out.println("Invalid");
}
}
private static void BotTurn() { //Bot turn -> (Modify these to change the AI behaviour, here's a very simple one);
boolean choose = true;
for (int y = 0; y < 3 ; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == empty&&choose) {
board[y][x] = circle;
choose = false;
}
}
private static void display() { //Display the board
int nn = 1;
String a = "z";
for (int y = 0; y < 3 ; y++) {
for (int x = 0; x < 3; x++) {
if (board[y][x] == 0) a = "*";
if (board[y][x] == 1) a = getCrosses;
if (board[y][x] == 2) a = getCircles;
System.out.print(a + " ");
}
System.out.print(" "); //Indications
for (int xn = 0; xn < 3; xn++) {
System.out.print(nn);
nn+=1;
System.out.print(" ");
}
System.out.println(" ");
}
}
}
How about this idea: (neither the only nor the best nor the most performant solution... just an idea)
You can use the sum of each row, diagonal and column to determine if the either player one (all 1s) or player two (all 2s) wins. Therefore you only need to set the empty field to be higher than 6.
For example let's say your board looks like this:
7 1 1 -> 7+1+1 = 9 // no one wins
2 2 2 -> 2+2+2 = 6 // player two wins, he has 3 * 2 in a row
1 7 2 -> 1+7+2 =10 // no win here
if all three numbers where 1s (sum == 3) your player one wins.
It is "cumbersome" to implement, but as I said it is just an idea:
// first we check every column
for( int x=0; x<board[y].length; x++){
int sum = 0;
for( int y=0; y<board.length; y++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// then every row
for( int y=0; y<board.length; y++){
int sum = 0;
for( int x=0; x<board[y].length; x++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// and finally the diagonals (if we ever reach that part)
int sum= board[0][0] + board[1][1] + board[2][2];
if(sum == 3 || sum == 6){
return true;
}
sum= board[0][2] + board[1][1] + board[2][0];
if(sum == 3 || sum == 6){
return true;
}
you could also return 1 when the sum == 3 and the first player wins or 2 when player two wins.
I am creating a puzzle game hashi, basically the purpose of the game is to solve a puzzle using connection (Bridges) and Nodes(Islands).
To solve the puzzle, the user has to connect the same amount of bridges to a corresponding nodes weight.
The user is allowed to join the islands using 2 bridges from one island, but after s/he tries to connect the 3 bridges from the same node the connection should disappear and the user should be allowed to create a new one from that node.
Problem:
The user can draw 2 bridges from the same node but once he tries to do a third one the connection disappear JUST VISUALLY meaning the nodes are still connected and then the user cannot draw a bridge again.
And in here the bridges are just deleted visually.
I tried to debug it, but unfortunately I cannot seem to figure out where the problem is.
Here is the code to my problem.
public class BoardCreation {
// This class member is used for random initialization purposes.
static private final Random random = new Random();
// The difficulty levels.
private static final int EASY = 0;
static public final int MEDIUM = 1;
static public final int HARD = 2;
static public final int EMPTY = 0;
private static int ConnectionFingerprint(BoardElement start, BoardElement end) {
int x = start.row * 100 + start.col;
int y = end.row * 100 + end.col;
// Swap to get always the same fingerprint independent whether we are called
// start-end or end-start
if (x > y ) {
int temp = x;
x = y;
y = temp;
}
Log.d("", String.format("%d %d" , x ,y));
return x ^ y;
}
public class State {
// The elements of the board are stored in this array.
// A value defined by "EMPTY" means that its not set yet.
public BoardElement [][] board_elements = null;
public int [][] cell_occupied = null;
// The width of the board. We only assume squared boards.
public int board_width=0;
public State(int width) {
board_width = width;
board_elements = new BoardElement[width][width];
cell_occupied = new int[width][width];
}
public State CloneWithoutConnections() {
State newstate = new State(board_width);
if (board_elements != null) {
newstate.board_elements = new BoardElement[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
for (int j = 0; j < board_elements.length; ++j) {
if (board_elements[i][j] == null)
continue;
newstate.board_elements[i][j] = board_elements[i][j].clone();
}
}
}
if (cell_occupied != null) {
assert board_elements != null;
newstate.cell_occupied = new int[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
System.arraycopy(cell_occupied[i], 0, newstate.cell_occupied[i], 0, board_elements.length);
}
}
return newstate;
}
public void AddToBridgeCache(BoardElement first, BoardElement second) {
if (first == null || second == null) { return; }
final int fingerprint = ConnectionFingerprint(first, second);
Log.d(getClass().getName(),
String.format("Fingerprint of this bridge %d", fingerprint));
// mark the end points as occupied.
cell_occupied[first.row][first.col] = fingerprint;
cell_occupied[second.row][second.col] = fingerprint;
int dcol = second.col - first.col;
int drow = second.row - first.row;
if (first.row == second.row) {
for (int i = (int) (first.col + Math.signum(dcol)); i != second.col; i += Math.signum(dcol)) {
cell_occupied[first.row][i] = fingerprint;
String.format("deleting bridge");
}
} else {
assert first.col == second.col;
for (int i = (int) (first.row + Math.signum(drow)); i != second.row; i+= Math.signum(drow)) {
cell_occupied[i][first.col] = fingerprint;
String.format("deleting bridge", fingerprint);
}
}
}
} // end of state
}
I'm having trouble randomizing and adding a 2x2 ship into the game board. I need it to look like the following:
currently I can only seem to get a 1x1 ship and don't quite understand the logic for adding the 2x2 and randomizing it so that they're all connected.
also when the user inputs a '2' at the main menu I need to show the solution, meaning where the ships are. Which I also could use some help on.
Not nearly finished but please be critical when it comes to judging my code, everything helps!
Thanks in advance.
import java.util.Scanner;
public class Battleship
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
int [][] board = new int [5][5];
int [][] ship = new int [4][2];
int [] shot = new int[2];
boolean done = false;
resetboard(board);
while(!done)
{
displayBoard(board);
displayMenu();
for(int ships=0 ; ships < 4 ; ships++)
{
ship[ships][0]=(int) Math.random() * 5 + 1;
ship[ships][1]=(int) Math.random() * 5 + 1;
}
int choice = getMenuInput(input);
if(choice == 1)
{
getRow(shot);
getColumn(shot);
if(fireShot(shot,ship) == true)
{
board[shot[0]][shot[1]]= 1;
}
else
{
board[shot[0]][shot[1]]= 0;
}
}
else if(choice == 2)
{
for (int x = 0; x < 5; x++)
{
for(int y = 0; y < 5; y++)
{
for(int z = 0; z < 3; z++)
{
if(board[x][y] == ship[z][0] && board[x][y] == ship[z][1] )
{
board[ship[z][0]][ship[z][1]]= 1;
}
}
}
}
displayBoard(board);
}
else if (choice == 3)
{
done = true;
System.out.println("Thanks For Playing");
}
}
}
public static void displayBoard(int [][] board)
{
System.out.println(" A B C D E");
for(int r =0; r < 5; r++)
{
System.out.print((r + 1) + "");
for(int c = 0; c < 5; c++)
{
if(board[r][c] == -1)
{
System.out.print(" -");
}
else if(board[r][c] == 0)
{
System.out.print(" X");
}
else if(board[r][c] == 1)
{
System.out.print(" *");
}
}
System.out.println("");
}
}
public static void resetboard(int[][] a)
{
for(int row=0 ; row < 5 ; row++ )
{
for(int column=0 ; column < 5 ; column++ )
{
a[row][column]=-1;
}
}
}
public static void displayMenu()
{
System.out.println("\nMenu:");
System.out.println("1. Fire Shot");
System.out.println("2. Show Solution");
System.out.println("3. Quit");
}
public static int getMenuInput(Scanner input)
{
int in = 0;
if(input.hasNextInt())
{
in = input.nextInt();
if(in>0 && in<4)
{
in = in;
}
else
{
System.out.println("Invalid Entry, Please Try Again.\n");
}
}
else
{
System.out.println("Invalid Entry, Please Try Again.\n");
input.nextInt();
}
return in;
}
public static void getRow(int [] shot)
{
Scanner input = new Scanner(System.in);
System.out.println("Enter a Row Number: ");
shot[0] = shotValid(input);
shot[0]--;
}
public static void getColumn(int [] shot)
{
Scanner input = new Scanner(System.in);
int numb = 0;
System.out.println("Enter a Column Letter: ");
String choice = input.next();
if (choice.equals("A"))
{
numb = 0;
}
else if(choice.equals("B"))
{
numb = 1;
}
else if( choice.equals("C"))
{
numb = 2;
}
else if(choice.equals("D"))
{
numb = 3;
}
else if(choice.equals("E"))
{
numb = 4;
}
else
{
System.out.println("2Invalid Entry, Please Try Again.\n");
input.nextLine();
}
shot[1] = numb;
}
public static boolean fireShot(int [] shot, int [][]ship)
{
boolean result = false;
for(int shipHit=0 ; shipHit<ship.length ; shipHit++)
{
if( shot[0]==ship[shipHit][0] && shot[1]==ship[shipHit][1])
{
result = true;
}else
{
result = false;
}
}
return result;
}
public static int shotValid(Scanner quantity)
{
int shot = 0;
boolean done = false;
while(!done)
{
if(quantity.hasNextInt())
{
shot = quantity.nextInt();
if(shot>0 && shot<6)
{
shot = shot;
done = true;
}
else
{
System.out.println("1Invalid Entry, Please Try Again.\n");
}
}
else
{
System.out.println("2Invalid Entry, Please Try Again.\n");
quantity.next();
}
}
return shot;
}
}
You want to place a single ship of size 2×2 on the board and do this:
for(int ships=0 ; ships < 4 ; ships++)
{
ship[ships][0]=(int) Math.random() * 5 + 1;
ship[ships][1]=(int) Math.random() * 5 + 1;
}
There are several errors here:
The random variables will always be 1, because the (int) conversion affects only the result of Math.random(), which is a pseudo-random floating-point number between 0 and 1 exclusively. Conversion to int truncates this to 0. Use (int) (Math.Random() * 5), which will yield a random number from 0 to 4.
You shouldn't add 1. Internally, your game uses the zero-base indices that Java uses, which is good. ()These are known to the outside as rows 1 to 5 ande columns A to E, but you take care of that in your getRow and getColumn functions.)
You place up to four independent ships of size 1×1. (This is up to four, because you might end up wit one ship in an already occupied place.)
To place a single 2×2 ship, just determine the top left corner randomply and make the other ship coordinates dependent on that:
int x = (Math.random() * 4);
int y = (Math.random() * 4);
ship[0][0] = x;
ship[0][1] = y;
ship[1][0] = x + 1;
ship[1][1] = y;
ship[2][0] = x;
ship[2][1] = y + 1;
ship[3][0] = x + 1;
ship[3][1] = y + 1;
You now have two separate data structures: The board, which is all minus ones initially, and the list of ships. Your display routine suggests that you want three different values for a cell in the board: −1 is water; 1 is an unarmed part of a ship and 0 is where a shot has been fired.
But you never set these values. You set the position of the ship before displaying, but you should probably set them straight away. You should also set the locations of shots, so that you never fire at the same cell.
You need two modes for displaying the board: The in-play mode, where the unharmed ships are displayed as water and the solution mode, which shows everything as it is. You could so this by passing a flag to the routine.
Now if you think about it, you don't really need the ship array. Just use the information in the board:
int x = (Math.random() * 4);
int y = (Math.random() * 4);
board[x][y] = 1;
board[x + 1][y] = 1;
board[x][y + 1] = 1;
board[x + 1][y + 1] = 1;
Keep a count of ships, initially 4. When you fire at water, mark the cell with 0. When you fire at a ship, mark the cell as 0 and decrement the count of ships. If the count of ships is zero, the player has won. Otherwise, redisplay the boatrd and shoot again.
im creating a code for Minesweeper and trying to implement a GUI. But the problem is that when i run the code and play the game, the position i click on the board reveals the y,x coordinate of that location on the answer board rather than the x, y coordinate. Ive been trying to fix this but i cant see to figure it out. i think it maybe is the way i create the board, but i tried everything i could think of.
class Board{
public MsGUI gui;
private static char[][] userBoard;
private static char[][] solutionBoard;
private static int boundSize = 5;
public Board(){
userBoard = new char[][] {{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'}};
solutionBoard = new char[][] {{'0','2','B','2','0'},
{'0','3','B','3','0'},
{'1','3','B','3','1'},
{'B','1','3','B','2'},
{'1','1','2','B','2'}};
return;
}
private static void printBoard(char[][] board){
for (int x = 0; x < boundSize; x++){
for(int y = 0; y < boundSize; y++){
System.out.print(" " + Character.toString(board[x][y]));
}
System.out.println("");
}
System.out.println("");
}
public void flagCell(int xCoordinate, int yCoordinate){
userBoard[xCoordinate][yCoordinate] = 'F';
}
public boolean isFlagged(int xCoordinate,int yCoordinate){
if(userBoard[xCoordinate][yCoordinate] == 'F'){
return true;
}
else{
return false;
}
}
public int getHeight() {
return userBoard.length;
}
public int getWidth(){
return userBoard[0].length;
}
public char getValue(int xCoordinate, int yCoordinate) {
return userBoard[xCoordinate][yCoordinate];
}
private static boolean checkIfAlreadyMarked(int xCoordinate, int yCoordinate)
{
boolean marked = false;
if (Character.toString(userBoard[xCoordinate][yCoordinate]).equals("-") == false)
{
marked = true;
}
return marked;
}
public void revealCell(int xCoordinate, int yCoordinate){
int count = 0;
for(int i = 0;i < userBoard.length;i++){
for(int J = 0;J < userBoard[i].length;J++){
if(userBoard[i][J] != '-'){
count = count + 1;
}
}
if(count == 19){
gui.win("you won");
return;
}
}
if(solutionBoard[xCoordinate][yCoordinate] == 'B'){
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
gui.lose("You lost. Better luck next time!");
return;
}
if(solutionBoard[xCoordinate][yCoordinate] != '0'){
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
}else{
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
for(int i = 1; i > -2; i--){
if(xCoordinate-i >= solutionBoard.length || xCoordinate-i < 0)
continue;
for(int z = 1; z > -2; z--){
if(yCoordinate-z >= solutionBoard[xCoordinate].length || yCoordinate-z < 0)
continue;
else if(userBoard[xCoordinate-i][yCoordinate-z] == 'F' || userBoard[xCoordinate-i][yCoordinate-z] != '-')
continue;
else{
revealCell(xCoordinate-i, yCoordinate-z);
}
}
}
}
}
public void unflagCell(int xCoordinate, int yCoordinate){
userBoard[xCoordinate][yCoordinate]='-';
}
public static void main(String[] args){
Board b = new Board();
b.gui = new MsGUI(b);
b.gui.setVisible(true);
}
}
The way you are initializing the solutionBoard is not what you expect it to be.
If you get solutionBoard[0], you're not accessing the first column(which would be consistent with what I think is your understanding), but the first row(first item of the two-dimensional array): {'0','2','B','2','0'}
So if you want to have x for a row index and y for a column index and still keep this "readable" initialization, you'll have to swap the indices whenever you access the array.
But this will only help you with one problem - human readable array assignment in the beginning, but I think you'll regret this decision in the future.
EDIT:
You can have the array initialized as you want and still use readable format like this:
String readableBoard =
"0 2 B 2 0;" +
"0 3 B 3 0;" +
"1 3 B 3 1;" +
"B 1 B B 2;" +
"1 1 2 B 2";
char[][] board = initBoard(readableBoard);
....
private char[][] initBoard(String readableBoard){
char[][] board = new char[5][5];
String[] rows = readableBoard.split(";");
String[] fields = null;
for (int y = 0; y<rows.length;y++){
fields = rows[y].split(" ");
for (int x = 0; x<fields.length; x++){
board[x][y]=fields[x].charAt(0);
}
}
return board;
}
Now when you call
board[2][0]
You'll get 'B'
If you look at your nested for loops, you're printing off the columns instead of what I assume to be the desired rows. Try switching your for loops to iterate over y, then x.
for (int y = 0; y < boundSize; y++){
for(int x = 0; x < boundSize; x++){
System.out.print(" " + Character.toString(board[x][y]));
}
System.out.println("");
}
System.out.println("");
I have been racking my brain around this problem for days now... I can't see what my algorithm could possibly be missing. This is the problem here.
From what I gather I am getting the points in a somewhat circular ccw order. So I've implemented a version of the graham scan that goes through looking to find the convex hull by ensuring it is using points that always give right-hand turns.
My algorithm works for all given test input and all input I can come up for it but it just won't get accepted by the online judge which is what is required for the assignment to be 'complete'.
Anyways, here is my code I would be forever in your debt if someone can find what I'm missing.
import java.util.Scanner;
import java.util.Vector;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public Main() {}
public void handlePoints(Point[] points) throws Exception {
int m = 1;
Vector<Point> convexHull = new Vector<Point>();
// This is THE ONLY gaurunteed point to be in the hull - and it is the lowest left point so that's ok.
convexHull.add(points[0]);
// Can be removed if ill-suited.
convexHull.add(points[1]);
for (int i = 2; i < points.length; i++) {
// Find the next valid point on the hull.
while (counterClockWise(convexHull.elementAt(m-1), convexHull.elementAt(m), points[i]) <= 0) {
convexHull.removeElementAt(m);
if (m > 1) {
m -= 1;
}
// All points are colinear
else if (i == points.length - 1) {
break;
}
else {
convexHull.add(points[i]);
i++;
}
}
convexHull.add(points[i]);
m++;
}
if (convexHull.size() <= 3) {
throw new Exception();
}
String test = "" + convexHull.size() + '\n';
for (Point p : convexHull) {
test += p.x + " " + p.y + '\n';
}
System.out.print(test);
}
// Simply calculated whether or not the 3 points form a countedClockWise turn.
public int counterClockWise(Point p1, Point p2, Point p3) {
return ((p2.x - p1.x) * (p3.y - p1.y)) - ((p2.y - p1.y) * (p3.x - p1.x));
}
// Rearranges the array to maintain its order but be started and ended by the point with the lowest y value
private static Point[] moveLowestToFront(Point[] array) {
// Rearrange for y:
int lowestY = 99999;
int lowestIndex = 0;
for (int i = 0; i < array.length; i++) {
if (array[i].y < lowestY) {
lowestY = array[i].y;
lowestIndex = i;
}
}
// Scan through again to see if there are any competing low-y values.
int lowestX = 99999;
for (int i = 0; i < array.length; i++) {
if (array[i].y == lowestY) {
if (array[i].x < lowestX) {
lowestX = array[i].x;
lowestIndex = i;
}
}
}
Point[] rearrangedPoints = new Point[array.length];
int j = 0;
// Take from low to end cutting off repeated start point.
for (int i = lowestIndex; i < array.length - 1; i++) {
rearrangedPoints[j] = array[i];
j++;
}
// Throw the remaining and put them at the end.
for (int i = 0; i < lowestIndex; i++) {
rearrangedPoints[j] = array[i];
j++;
}
// End the array with the repeated first point.
rearrangedPoints[array.length - 1] = array[lowestIndex];
return rearrangedPoints;
}
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
Main convexHullFinder = new Main();
int numDataSets = sc.nextInt();
System.out.println(numDataSets);
for (int z = 0; z < numDataSets; z++) {
int numPoints = sc.nextInt();
Vector<Point> points = new Vector<Point>();
// Read in all the points for this set.
points.add(new Point(sc.nextInt(), sc.nextInt()));
int j = 1;
for (int i = 1; i < numPoints; i++) {
Point p = new Point(sc.nextInt(), sc.nextInt());
// Remove repeated points.
if (p.x < 0 || p.y < 0) {
throw new Exception();
}
if ( (p.x == points.elementAt(j-1).x) && (p.y == points.elementAt(j-1).y) ) {}
else {
points.add(p);
j++;
}
}
Point[] reducedPoints = points.toArray(new Point[points.size()]);
// Rearrange the set to start and end on the lowest Y point.
reducedPoints = moveLowestToFront(reducedPoints);
if (numPoints >= 3) {
convexHullFinder.handlePoints(reducedPoints);
}
else {
throw new Exception();
}
try {
System.out.println(sc.nextInt());
}
catch (Exception e) {
}
}
}
}
class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
From the sounds of it, the points are sorted such that Graham Scan applies. Therefore, I think your stack operation (handlePoints) is probably not right.
I'm more used to Andrew's algorithm (a modification of Graham Scan), but I'm fairly sure you shouldn't be adding points to the convex hull both inside and outside of the while loop. The reason being I'm fairly sure the purpose of the while loop remains the same regardless of which algorithm is used. It's to remove invalid points from the convex hull. However, there's a chance that you're adding points during the while loop.
I'm not sure that's all that needs fixed though, but I don't have anything set up to run Java at the moment.