I am trying to play tic tac toe using iterative Alpha-Beta prunning,
I have one second limit for a move but for some reason it
doesnt work well.
I modified the regular alpha-beta code so instead of returning
alpha or beta, it returns a state (which is the board with the next move)
Each time I create children I update their depth.
But again for some reason I keep losing and I see that
my alpha beta doesnt see the best move to make.
Here is my code:
The outer loop:
while (watch.get_ElapsedMilliseconds() < 900 && d <= board.length * board[0].length - 1)
{
s = maxiMin(beginSt, d, watch);
if (s.getNextMove().getIsWin() == true)
{
break;
}
d++;
}
return new location(s.getNextMove().getRow(), s.getNextMove().getCol());
The alpha beta:
public State maxiMin(State s, int depth, Stopwatch timer)
{
if (s.getDepth() == 7)
{
Console.WriteLine();
}
if (timer.get_ElapsedMilliseconds() > 850 || s.getDepth() == depth || goalTest(s.getBoard()) != 0)
{
s.evaluationFunc(line_length, PlayerShape);
s.setAlpha(s.getEvaluation());
s.setBeta(s.getEvaluation());
return s;
}
LinkedList<State> children = createChildren(s, true);
// No winner, the board is full
if (children.get_Count() == 0)
{
s.evaluationFunc(line_length, PlayerShape);
s.setAlpha(s.getEvaluation());
s.setBeta(s.getEvaluation());
return s;
}
while (children.get_Count() > 0)
{
State firstChild = children.get_First().get_Value();
children.RemoveFirst();
State tmp = miniMax(firstChild, depth, timer);
int value = tmp.getBeta();
if (value > s.getAlpha())
{
s.setAlpha(value);
s.setNextMove(tmp);
}
if (s.getAlpha() >= s.getBeta())
{
return s;
}
}
return s;
}
public State miniMax(State s, int depth, Stopwatch timer)
{
if (s.getDepth() == 7)
{
Console.WriteLine();
}
if (timer.get_ElapsedMilliseconds() > 850 || s.getDepth() == depth || goalTest(s.getBoard()) != 0)
{
s.evaluationFunc(line_length, PlayerShape);
s.setAlpha(s.getEvaluation());
s.setBeta(s.getEvaluation());
return s;
}
LinkedList<State> children = createChildren(s, false);
// No winner, the board is full
if (children.get_Count() == 0)
{
s.evaluationFunc(line_length, PlayerShape);
s.setAlpha(s.getEvaluation());
s.setBeta(s.getEvaluation());
return s;
}
while (children.get_Count() > 0)
{
State firstChild = children.get_First().get_Value();
children.RemoveFirst();
State tmp = maxiMin(firstChild, depth, timer);
int value = tmp.getAlpha();
if (value < s.getBeta())
{
s.setBeta(value);
s.setNextMove(tmp);
}
if (s.getAlpha() >= s.getBeta())
{
return s;
}
}
return s;
}
Would appriciate much if anyone can tell me if something is wrong. I suspect maybe
it something to do with that I am returning "s" instead of the regular alpha beta
which returns the evaluation but I didnt manage to find the error.
Thanks in advance,
Lena
Firstly tic-tac-toe is a very simple game, and I believe it is solvable with a much simpler code, mainly because we know there is always a tie option and the total number of states is less then 3^9 (including symmetrical and many impossible states).
As for your code I believe one of your problems is that you don't seem to increment your depth in the recursive calls.
you also have many issues of bad style in your code, you separated miniMax and MaxiMin into two functions though they are fundamentally the same. you iterate over a collection by removing elements from it as opposed to using for-each or an iterator(or even an int iterator).
Related
Note: I am a beginner in Java (2 - 3 months of experience).
Doing a project on JetBrains/Hyperskill about making a Tic Tac Toe game, I found myself repeating quite a bit of code when trying to determine the winner of the game. To represent the game as a coordinate system (Thus 1,1 being at the bottom left and 3,3 at the top right) I am using a two-dimensional array.
This is the function for determining the winner:
public String determineWinner() {
int countX = 0; // amount of X's in a row
int countO = 0; // amount of O's in a row
for (int y = 0; y <= 2; y++) { // for all horizontal rows
countX = 0;
countO = 0;
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
String value = this.field[x][y];
if (value.equals("X")) { // if the value at that coordinate equals "X", add 1 to the count
countX++;
}
if (value.equals("O")) { // same here
countO++;
}
}
if (countX == 3) { // if the count is 3 (thus 3 X's in a row), X has won
return "X wins";
}
if (countO == 3) { // same here
return "O wins";
}
}
// Same thing, but for all vertical columns
for (int x = 0; x <= 2; x++) {
countX = 0;
countO = 0;
for (int y = 0; y <= 2; y++) {
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
}
// Same thing, but for diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
// Same thing, but for other diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
if (this.getNumberOfMoves() == 9) { // if the number of moves equals 9, the game is over and it is a draw
return "draw";
}
return "game not finished";
}
Currently, the code allows you to set a starting board (a starting arrangement for all the O's and X's) and then lets you do 1 move. After this, the game decides who is the winner or if it is a draw etc.
As one quickly notices, the function is way too long and it has quite a portion of repetition, yet I am unable to come up with any ways to shorten it.
Does anyone have any tips? Or any guidelines that apply to all code?
DISCLAIMER: Sorry if my answer started getting sloppy towards the end.
Also, I have a code at the bottom showing all the things I talked about in action.
I think the simplest thing I can say is to use more methods and possibly classes. Firstly, one of the ways to avoid repetition in all of your codes is to write them using object-oriented programming. This is the idea of having multiple classes that all interact with the main class to assist in writing code. I won't talk about that here, but if you are interested in making your code neat and "clean", I highly advise looking that up. Also, there is a great book on the subject called Clean Code by Robert C. Martin. I will simply be showing how you can take advantage of methods to shorten your code and clean it up. One of the things you repeat the most is this
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
Your countX and countO are different each time, so you rewrote it. I simpler and more efficient way to do this is to use a method. I would advise you to research the syntax for Java in you don't know how to make methods or classes, but you do use the syntax for the determineWinner() method so I will assume you understand it. You can make functions have parameters that are essentially inputs that can be accessed and modified throughout the function. (By the way, you cannot make methods inside methods in Java so you would need to place this next method outside somewhere else in the class.)
public String checkCounts() {
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
else return "N/A";
}
*You want to check to see if it returns "N/A" anytime you use the method with an if statement. If so, you should just ignore it since no one won.
whoWon = checkCounts();
//In the code I put at the bottom I will make whoWon a global variable, which is why I'm not defining it here.
//It will be already defined at the top of the code.
if (!whoWon.equals("N/A")) return whoWon;
*The ! symbol means not, a.k.a if whoWon does NOT equal "N/A", return whoWon.
This way, anytime you need to write out that if statement code, you can just write checkCounts and plug in the two variables that you just got from your Array. You would write checkCounts(); in this case. Now if you just say return checkCounts(); then the code will run all those if statements without you having to type them all and return the result. You actually repeat something else a lot too. These couple of lines
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
are quite similar to these lines
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
and these lines
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
so you can condense them all down into one method with three different inputs. The method will return either 0, 1, or 2. The goal is to check which one it returns with the given string input and then translate that to which variable to add 1 to.
If it's 0, ignore, if it's 1, countX++, and if it's 2, countY++.
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) {
whichCount = 1;
}
if (value.equals("O")) {
whichCount = 2;
}
return whichCount;
}
Switch statements might be a little advanced, but they're pretty simple in concept. It's a bunch of if statements all at once in a very convenient syntax. The value inside the parenthesis is your input, or what to check. The cases say, when its equal to this, do this. When you needed to increment either countX or countY inside your for loops, you would write
switch (checkString(this.field[coord1][coord2])) {
case 1 -> countX++;
case 2 -> countO++;
}
case 1 says, if addToCount() returns 1 then do the thing to the right of the arrow and case 2 says if it returns 2 to the thing to the right of that arrow. In your for loops, coord1 and coord2 could be anything from [x][y] to [i][i] to [i][2-i] so you can change that anytime you make the switch statement.
Additionally, you can turn that switch statement itself into a method.
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
You can also take a couple of lines off by shorting your if statements. If the thing inside the if statement is only one line long than you can just put in next to it.
if (bool) {
doSomething();
}
//Change that to this
if (bool) doSomething();
Another thing you repeat a lot is this
countX = 0;
countO = 0;
I just made a very simple method that does that with no parameters.
public void resetCounts() {
countX = 0;
countO = 0;
}
That's pretty much it for repetition, but I would argue your determineWinner method is still far too large. Even if you don't repeat any more code, taking large changes of it and separating it into smaller bites can make it easier to read and understand.
I added in a bunch of methods that just contained your for loops. They will be at the very bottom of this final class I came up with. It's 85 lines long so it's technically only a 4 line improvement but it's a lot cleaner. Additionally, if you were to embed this in your actual class, and not just in a single method (because you can't put it all in one method) then it would be even more efficient because you would have access to all of the classes global variables. Here is the code I came up with, but I would highly recommend doing extra research on object-oriented programming to really improve your code.
public class TicTacToe {
String[][] field = new String[3][3];
int countX, countO = 0; // amount of X's and O's in a row
String whoWon = "N/A";
public int getNumberOfMoves() {return 0;} //Whatever you method did that determined this. Obviously it didn't really just return 0.
public String determineWinner() {
String columns = checkColumnsForWinner();
String rows = checkRowsForWinner();
String diagonal1 = checkDiagonal(1, 0);
String diagonal2 = checkDiagonal(-1, 2);
if (checkForNA(columns)) return columns;
if (checkForNA(rows)) return rows;
if (checkForNA(diagonal1)) return diagonal1;
if (checkForNA(diagonal2)) return diagonal2;
if (this.getNumberOfMoves() == 9) return "draw"; // if the number of moves equals 9, the game is over and it is a draw
return "game not finished";
}
public String checkCounts(int countX, int countO) {
if (countX == 3) return "X wins";
if (countO == 3) return "O wins";
else return "N/A";
}
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) whichCount = 1;
if (value.equals("O")) whichCount = 2;
return whichCount;
}
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
public void resetCounts() {
countX = 0;
countO = 0;
}
public String checkRowsForWinner() {
for (int y = 0; y <= 2; y++) { // for all horizontal rows
resetCounts();
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkColumnsForWinner() {
for (int x = 0; x <= 2; x++) {
resetCounts();
for (int y = 0; y <= 2; y++) {
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkDiagonal(int mutiply, int add) {
resetCounts();
for (int i = 0; i <= 2; i++) {
adjustCounts(field[i][i*mutiply + add]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
return "N/A";
}
public boolean checkForNA(String string) {return !string.equals("N/A");}
}
In regards to Object-Oriented Programming, the best example I could see you put into practice in this example is Abstraction. This is a very general concept but I think it would help a lot in this case. In my program above, I have a TicTacToe class, and all of my code in it. The problem is, you are seeing a lot of boilerplate to get the code to run. The biggest example is the 2D Array object you have. You have to do so many things to get X's or O's out of it. It would be much better (opinion) to make a new class, maybe called Board. It would contain a private 2D Array object, and public methods to get values from that object. Additionally, (this is really just my opinion) I would recommend using an enumeration instead of Strings for you Array values. For example
public enum BoardValues {
X,
O,
EMPTY
}
You could then create a class to place these board values in essentially a 3x3 Grid.
public class Board {
private BoardValues[][] values = new BoardValues[3][3];
public BoardValues getValue(int x, int y) {
return values[x][y];
}
public BoardValues[] getRow(int rowNumber) {
BoardValues[] rowValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
rowValues[i] = getValue(i, rowNumber);
}
return rowValues;
}
public BoardValues[] getColumn(int columnNumber) {
BoardValues[] columnValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
columnValues[i] = getValue(columnNumber, i);
}
return columnValues;
}
public void setValues(BoardValues[][] values) {
this.values = values;
}
public void setValue(int x, int y, BoardValues value) {
values[x][y] = value;
}
}
Now instead of using that pesky old 2D Array you just create a board object and set and get it's values at will when needed. Also, I didn't add in getting diagonals but you still could quite easily, mine's just for proof of concept. This is Abstraction, probably the easiest of the OOP concepts to grasp, because it's so general. I am simply obscuring information you don't need to see when you're trying to code your game.
I am a beginner in preparing interview questions. I recently have a question about iterating a string.
When dealing with questions like "Valid Palindrome" and similar questions, we usually have 2 ways to solve the question.
We either keep updating pointers until we find the target char:
s = s.toLowerCase();
int lo = 0;
int hi = s.length() - 1;
while(hi > lo){
while(lo < hi && !Character.isLetterOrDigit(s.charAt(lo))) lo ++;
while(hi > lo && !Character.isLetterOrDigit(s.charAt(hi))) hi --;
if(s.charAt(lo) != s.charAt(hi)) return false;
lo ++;
hi --;
}
return true;
Or just iterating the string (from leetcode discussion):
int head = 0, tail = s.length() - 1;
char cHead, cTail;
while(head <= tail) {
cHead = s.charAt(head);
cTail = s.charAt(tail);
if (!Character.isLetterOrDigit(cHead)) {
head++;
} else if(!Character.isLetterOrDigit(cTail)) {
tail--;
} else {
if (Character.toLowerCase(cHead) != Character.toLowerCase(cTail)) {
return false;
}
head++;
tail--;
}
}
return true;
I am not sure which method is better in terms of big O analysis and which one to use during an interview?
Thanks in advance!
The second is better.
The first also treats lo == hi and
repeats the condition of the outer loop.
Also charAt is repeated for the same index. (Though in the second cTail might not have been gotten.)
The second is less complex, lazier, handling small cases, small steps, easily verifiable.
The second could be written in nicer style as:
//char cHead, cTail;
while(head <= tail) {
char cHead = s.charAt(head);
char cTail = s.charAt(tail);
As a declaration inside a loop is no overhead, just a single stack variable is reserved for a variable.
I work on a genetic algorithm for a robotic assembly line balancing problem (assigning assembly operations and robots to stations to minimize the cycle time for a given number of stations). The solution is represented by an ArrayList (configuration) which holds all the operations in the sequence assigned to different stations. Furthermore, I have two more ArrayLists (robotAssignment, operationPartition) which indicate where a new station starts and which robot is assigned to a station. For example, a solution candidate looks like this (configuration, robotAssignment, operationPartition from top to bottom):
Initial cycle time: 50.0
|2|7|3|9|1|5|4|6|8|10|
|2|1|3|2|
|0|2|5|7|
From this solution representation we know that operations 3, 9, and 1 are assigned to the second sation and robot 1 is used.
I need to keep track of the station an operation is assigned to. I tried a lot to store this in the Object Operation itself but I always ended up in problems and therefore I want to write a method that gives me the stations index of an operation.
Here is what I have coded so far:
// Get the station of an operation
public int getStation(Operation operation) {
int stationIndex = 0;
int position = configuration.indexOf(operation);
for (int i = 0; i < GA_RALBP.numberOfStations ; i++ ) {
if (i < GA_RALBP.numberOfStations - 1 && operationPartition.get(i) != null) {
if (isBetween(position, (int) operationPartition.get(i), (int) operationPartition.get(i + 1))) {
return stationIndex + 1;
} else {
stationIndex++;
}
}
else if (i >= GA_RALBP.numberOfStations - 1 && operationPartition.get(i) != null) {
if (isBetween(position, (int) operationPartition.get(i), configurationSize())) {
return stationIndex + 1;
}
}
}
return -1;
}
// Check if value x is between values left and right including left
public static boolean isBetween(int x, int left, int right) {
if (left <= x && x < right ) {
return true;
}
else {
return false;
}
}
However, this does not seem to be (a) very elegant and (b) if I have to do this for a large number of operations the runtime could become a problem. Has anoyone an idea how to solve this more efficiently?
Why not make the partitioning explicit (replaces your operationPartition) - something like:
Map<Integer, Integer> operationToStationMapping = new HashMap<>();
operationToStationMapping.put(2,0);
operationToStationMapping.put(7,0);
operationToStationMapping.put(3,2);
operationToStationMapping.put(9,2);
operationToStationMapping.put(1,2);
operationToStationMapping.put(5,5);
operationToStationMapping.put(6,7);
operationToStationMapping.put(8,-1);
operationToStationMapping.put(10,-1);
Then getStation() becomes:
getStation(int operation) {return operationToStationMapping.get(operation);}
I'm currently working on an assignment where I am supposed to write mergeSort using stacks! I have a pretty good idea on how stacks and mergesort works, however I am not sure how to finish my code with stacks. I've set up a base case, and here I'm trying to use an in-place method where I just theoretically divide up my array without creating a new one by changing the markers. I am pretty new to this and unsure of how I should proceed..
I think this is the sort of thing I'm going for :
Base Case --> PC = 1
if (callStack.peek().PC == 1) {
if (callStack.peek().start == callStack.peek().stop) { //length <=1
callStack.pop(); //either done, or array length was 1
merge(A, callStack.peek().start, callStack.peek().mid, callStack.peek().stop);
if (callStack.empty()){
break;
}
callStack.peek().PC++;
} else {
callStack.peek().PC++;
}
continue;
}`
any left divided array --> PC = 2
any right divided array --> PC == 3
int mid = (callStack.peek().stop-callStack.peek().start)/2;
if (callStack.peek().PC == 2) {
if (callStack.peek().start != callStack.peek().stop) {
current = new ProgramFrame(callStack.peek().start, callStack.peek().mid, 1);
callStack.push(current);
continue;
}
}
if (callStack.peek().PC == 3) {
if (callStack.peek().start != callStack.peek().stop) {
current = new ProgramFrame(callStack.peek().mid+1, callStack.peek().stop, 1);
callStack.push(current);
continue;
}
}
the merged of both --> PC ==4
if (callStack.peek().PC == 4) {
merge(A, callStack.peek().start, callStack.peek().mid, callStack.peek().stop);
callStack.pop();
if (!callStack.empty()) {
if (callStack.peek().PC == 2) callStack.peek().start callStack.peek().mid; //help??
if (callStack.peek().PC == 3) callStack.peek().mid+1, callStack.peek().stop; //help??
callStack.peek().PC++;
continue;
I'm sorry this is such a long post :( I am just really unsure of how to fix it and how to continue it...
** also merge and my programeFrame look okay, but if you need to see them I can send them too!
Project Euler 15 (spoilers):
I solved this problem by realizing that it was a sequence of central binomial coefficients. Another good way is through dynamic programming. Nonetheless, it seemed so natural to do recursively, that I did it anyway.
Here's my solution:
public long getNumberOfPaths()
{
getPaths(board[0][0]); //2D array of Positions
return count; //instance member (long)
}
private void getPaths(Position p)
{
if (p.hasDown())
{
getPaths(p.getDown());
}
if (p.hasRight())
{
getPaths(p.getRight());
}
if ((p.getRow() == board.length - 1) && (p.getColumn() == board.length -1))
{
count++;
}
}
NB: Size of board is: 1 + inputSize, so in this case it would be 21, since we have a 20x20 grid. This is because solving the above 2x2 problem is equivalent to solving the 3x3 problem, but going through the squares instead of traveling on their borders.
The logic of getPaths(Position p) is: go down for as long as you can, then go right for as long as you can. Once you hit the bottom right Position, add 1 to number of paths (count), go back to where you last stepped down and now instead of going down, go right instead (if you can't go right, backtrack again, etc). Repeat process. Of course, the recursion itself is keeping track of all of this. If it's not clear, or if anyone want to screw with working code, there are two, small, classes here. Adding a few print statements to getPaths(Position p) should make what's going on pretty obvious.
Anyway, this all works properly, my question is how to implement this without using count. Again, as I stated above, I know that there are better ways to solve this problem, that's not my issue. My issue is trying to get the same functionality as above, but without using an auxiliary variable. This would mean changing getPaths(Position p) from void to making it return a long. It may be a simple fix, but I'm just not seeing it right now. Thanks in advance.
Essentially I want the recursive calls them selves to keep track of the count, not any sort of actual counter.
I believe this should work
private long getPaths(Position p) {
return (p.hasDown() ? getPaths(p.getDown()) : 0) +
(p.hasRight() ? getPaths(p.getRight()) : 0) +
((p.getRow() == board.length - 1) && (p.getColumn() == board.length -1) ? 1 : 0);
}
Without using the auxiliary variable:
public long getNumberOfPaths()
{
return getPaths(new Position(0,0)); //2D array of Positions
}
private long getPaths(Position p)
{
long result= 0;
if (p.hasDown())
{
result+= getPaths(p.getDown());
}
if (p.hasRight())
{
result+= getPaths(p.getRight());
}
if ((p.getRow() == board.length - 1) && (p.getColumn() == board.length -1))
{
result+= 1;
}
return result;
}
Try this then:
private long getPaths(Position p)
{
return (p.hasDown() ? getPaths(p.getDown()) : 0) +
(p.hasRight() ? getPaths(p.getRight()) : 0) +
((p.getRow() == board.length - 1) &&
(p.getColumn() == board.length -1) ? 1 : 0);
}
You could simply change your method signature to keep count as a parameter:
private long getPaths(Position p, long count) {
if (p.hasDown()) {
getPaths(p.getDown(), count);
}
if (p.hasRight()) {
getPaths(p.getRight(), count);
}
if ((p.getRow() == board.length - 1) && (p.getColumn() == board.length - 1)) {
count++;
}
return count;
}