testing tic tac toe win condition [duplicate] - java

This question already has answers here:
Algorithm for Determining Tic Tac Toe Game Over
(26 answers)
Closed 9 years ago.
I'm looking for the most efficient java way to test if somebody has won at tic tac toe. The data is in a 2d array like so...
char[][] ticTacToe =
{{'X',' ','O'},
{'O','X','O'},
{'X',' ','X'},};
I know this isn't the professional way to initialize an array but I'm just testing here.
The best I can do for right now is an exhaustive if/else tree.
Here's one of those trees...
if (ticTacToe[1][1] == 'X'){
if (ticTacToe[0][0] == 'X'){
if (ticTacToe[2][2] == 'X'){
System.out.println("X wins");
}
}
else if (ticTacToe[0][1] == 'X'){
if (ticTacToe[2][1] == 'X'){
System.out.println("X wins");
}
}
else if (ticTacToe[1][0] == 'X'){
if (ticTacToe[1][2] == 'X'){
System.out.println("X wins");
}
}
else if (ticTacToe[2][0] == 'X'){
if (ticTacToe[0][2] == 'X'){
System.out.println("X wins");
}
}
}
This one only cares about what's in the middle
This is very basic and I want to improve it as far as minimizing lines of code goes.

Just for fun, keep two numbers, starting as zeros, one for X, one for O. Update them by oring with the moves. To check for a winner, first and, then xor with the mask.
277 & 273 ^ 273
0 ==> we have a winner.
276 & 273 ^ 273
1 ==> not.
277 == parseInt("100010101",2)
273 == parseInt("100010001",2)
276 == parseInt("100010100",2)
For more fun, here's an example that plays O in your favorite JavaScript console:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<body>
<script>
var x = 0, o = 0, count = 0, w = 0
ws = [0007,0070,0700,0111,0222,0444,0124,0421]
function t1(v){
var w1 = 0
for (var i in ws)
w1 |= !(v & ws[i] ^ ws[i])
return w1
}
function t(i){
var ot = count % 2, m = 1 << (9 - i), bd = x | o
if (!ot && (i > 9 || i < 1 || i != Math.floor(i)))
return "Out of bounds."
else if (m & bd)
return "Position taken."
if (ot){
var n1 = 0, a1 = -2
while (bd & (1 << n1))
n1++
var n = n1
while (n1 < 9){
var m1 = 1 << n1
if (!(bd & m1)){
var bt = -mx(x,o | m1,count + 1)
if (bt > a1){
a1 = bt
n = n1
}
}
n1++
}
w = t1(o |= 1 << n)
}
else
w = t1(x |= m)
var b = "\n", p = 0400
while (p > 0){
if (p & x)
b += "X"
else if (p & o)
b += "O"
else b += "."
if (p & 0110)
b += "\n"
p >>= 1
}
if (w)
b += "\n\n" + (ot ? "O" : "X") + " wins!"
else if (!(bd ^ 0777))
b += "\n\nDraw."
if (!ot){
console.log(b + '\n\n"""')
count++
console.log(t(-1))
count++
}
else
return b + "\n"
return '"'
}
function mx(x1,o1,c1){
var ot1 = c1 % 2, w1 = ot1 ? t1(x1) : t1 (o1),
b1 = x1 | o1, p = 0400
if (w1)
return -1
if (!(b1 ^ 0777))
return 0
var a = -2
while (p > 0){
if (!(b1 & p))
a = Math.max(a,-mx(ot1 ? x1 : x1 | p,ot1 ? o1 | p : o1,c1 + 1))
p >>= 1
}
return a
}
console.log(' Plays O!'
+ '\nTo play, type t(MOVE); MOVE is from 1-9')
</script>
</body>
</html>

Mark board as 3x3 magicSquare and you have win when sum in line is 15.

It's a bit verbose, but I think this is probably the most efficient way to do it (unless someone can come up with a clever way to check both diagonals at once).
public class TicTacToe
{
char[][] ticTacToe =
{{'X',' ','O'},
{'O','X','O'},
{'X',' ','X'},};
private Character winner = null;
public Character getWinner()
{
return this.winner;
}
public boolean isSolved()
{
this.checkSolved();
return this.winner != null;
}
private void checkSolved()
{
for(int i = 0; i < ticTacToe.length; i++)
{
Character win = checkRow(i);
if(win != null || (win = checkColumn(i)) != null)
{
this.winner = win;
return;
}
}
//Check diagonal top left to bottom right
if(this.ticTacToe[0][0] != ' ')
{
if(this.ticTacToe[0][0] == this.ticTacToe[1][1] &&
this.ticTacToe[1][1] == this.ticTacToe[2][2])
{
this.winner = this.ticTacToe[0][0];
}
}
//Check diagonal top right to bottom left
else if(this.ticTacToe[0][2] != ' ')
{
if(this.ticTacToe[0][2] == this.ticTacToe[1][1] &&
this.ticTacToe[1][1] == this.ticTacToe[2][0])
{
this.winner = this.ticTacToe[0][2];
}
}
}
private Character checkRow(int row)
{
if(this.ticTacToe[row][0] == ' ')
{
return null;
}
if(this.ticTacToe[row][0] == this.ticTacToe[row][1] &&
this.ticTacToe[row][1] == this.ticTacToe[row][2])
{
return this.ticTacToe[row][0];
}
return null;
}
private Character checkColumn(int column)
{
if(this.ticTacToe[0][column] == ' ')
{
return null;
}
if(this.ticTacToe[0][column] == this.ticTacToe[1][column] &&
this.ticTacToe[1][column] == this.ticTacToe[2][column])
{
return this.ticTacToe[column][0];
}
return null;
}
public static void main(String[] args)
{
TicTacToe ttt = new TicTacToe();
if(ttt.isSolved())
{
System.out.println(ttt.getWinner()); // X
}
}
}

For a player, say 'x', there are 8 ways to win and each corresponds to 3 'x' in a row/column/diagonal.
Hence, you can create an array of length 8 and each item corresponds to the number of 'x' in that row/column/diagonal.
When the player chooses a move, then you update the array and check whether there exists 3 in the array.
Although it needs more space, it is easier to generalize to a large board.

There are four different ways to win at tick-tack-toe:
form a horizontal line
form a vertical line
form a diagonal line from the upper-left to the lower-right corner
form a diagonal line from the lower-left to the upper-right corner
All of these four win-conditions can be solved with a for-loop. The advantage of this solution is that it can be applied to any matrix-size.

Related

Pacman Collisions in Java

I am making a pacman arcade game on Java but my collisions have problems.
If you see my image
here my pacman sprite stands still (no x or y movement) when visiting a corner or when trying to turn back the direction it came (ie goes left but does not go right again). I understand this is because I set my xMovement to 0 and yMovement to 0 if a collision is detected (see second code block under collide()).
How would I allow the pacman sprite to move the other way (ie it comes from the left and I want it to go right) or traverse a corner if my collision() does not allow this?
Below is my draw method in the App.java class which draws the player based on the user's input. The keys wasd correspond with up, left, down, right respectively. this.direction is only used so the player does not waste a move nor go through a wall when a collision happens. *Note, I put (this.player.x_ord + 2) % 16 == 0 because my pacman image is not a 16x16 image unlike my walls which are.
public void draw() { // This is an infinite loop
mapDraw(); // this draws my map constantly so I do not have multiple pacman sprites drawn
this.player.tick(); // see second code below for details
if (keyPressed){ // Used wasd rather than arrow keys
if (key == 's' && this.direction != 's' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){ // We dont want Player to turn ever on a non 16 divisible area
this.player.p = this.loadImage("src/main/resources/playerDown.png");
this.player.yMovement = this.player.speed;
this.player.xMovement = 0;
this.direction = 's';
}
else if (key == 'w' && this.direction != 'w' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerUp.png");
this.player.yMovement = -this.player.speed;
this.player.xMovement = 0; // I do not want my pacman to move diagonally so thats why
this.direction = 'w';
}
else if (key == 'd' && this.direction != 'd' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerRight.png");
this.player.xMovement = this.player.speed;
this.player.yMovement = 0;
this.direction = 'd';
}
else if (key == 'a' && this.direction != 'a' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerLeft.png");
this.player.xMovement = -this.player.speed;
this.player.yMovement = 0;
this.direction = 'a';
}
}
this.player.draw(this); //see second code below for details
}
Below is my player.java class
*Note again that this.x_ord + 2 == w.x_pos + 16 && this.y_ord + 4 == w.y_pos is offset because of my pacman sprite is larger than my 16x16 walls.
public void tick(){
// //logic
this.detectCollision();
if (this.isLiving){
this.y_ord += this.yMovement;
this.x_ord += this.xMovement;
}
}
public void draw(PApplet a){ //just draw the sprite
if (this.isLiving){
a.image(this.p, this.x_ord, this.y_ord);
}
}
public void collide(boolean isX){ // Is it an x or y collision
if (isX == true){ // If it moves left or right into a wall
this.xMovement = 0;
}
else if (isX == false){ // If it moves up or down into a wall
this.xMovement = 0;
}
}
public void detectCollision(){
for (Walls w: this.wallLocations){ // A list of wall locations from a .txt file
if (this.x_ord + 2 == w.x_pos + 16 && this.y_ord + 4 == w.y_pos){ // Detect left movement into right wall piece
collide(true);
}
if (this.x_ord + 2 + 16 == w.x_pos && this.y_ord + 4 == w.y_pos){ // Detect right movement into left wall piece
collide(true);
}
if (this.y_ord + 4 == w.y_pos + 16 && this.x_ord + 2 == w.x_pos){ // Detect up movement into bottom wall piece
collide(false);
}
if (this.y_ord + 4 + 16 == w.y_pos && this.x_ord + 2 == w.x_pos){ // Detect down movement into top wall piece
collide(false);
}
}
Any help to my problem is much appreciated.
I didn't analyze your code deeply but it seems to me that your main problem is that collide method considers only 2 cases, vertical movement or horizontal movement. If you want to have movement in 4 different directions that method need to have 4 different states.
To achieve that you could create an enum representing direction. Then in detectCollision pass appropriate Direction into collide. Finally, in the collide consider 4 different directions. For example, if there is barrier on the right, xMovement need to be non-positive. The collide method could look like so:
public void collide(Direction direction){
if (direction == Direction.RIGHT){
this.xMovement = Math.min(0, this.xMovement);
}
if (direction == Direction.LEFT){
this.xMovement = Math.max(0, this.xMovement);
}
if (direction == Direction.UP){
this.yMovement = Math.min(0, this.yMovement);
}
if (direction == Direction.DOWN){
this.yMovement = Math.max(0, this.yMovement);
}
}

Check if a given sequence of moves for a robot is circular or not in Java

I am working on below task:
Given a sequence of moves for a robot, check if the sequence is circular or not. A sequence of moves is circular if first and last positions of robot are same.
A move can be on of the following.
G - Go one unit
L - Turn left
R - Turn right
Examples:
Input: path[] = "GLGLGLG"
Output: Given sequence of moves is circular
Input: path[] = "GLLG"
Output: Given sequence of moves is circular
The movements described in the input string are repeated for an infinite time. Your task is to find if there exists a circle, whose radius is some positive real number R,
such that the robot never leaves it. If such a circle exists return "YES" otherwise "NO"
I found a solution for this task :
public class Circle {
String check(String commands) {
int initialX = 0;
int initialY = 0;
int x = 0;
int y = 0;
String direction = "north";
for (int i = 0; i < commands.length(); i++) {
if (direction.equals("north")) {
if (commands.charAt(i) == 'G') {
y++;
} else if (commands.charAt(i) == 'L') {
direction = "west";
} else if (commands.charAt(i) == 'R') {
direction = "east";
} else {
System.out.println("Wrong command");
}
} else if (direction.equals("east")) {
if (commands.charAt(i) == 'G') {
x++;
} else if (commands.charAt(i) == 'L') {
direction = "north";
} else if (commands.charAt(i) == 'R') {
direction = "south";
} else {
System.out.println("Wrong command");
}
} else if (direction.equals("south")) {
if (commands.charAt(i) == 'G') {
y--;
} else if (commands.charAt(i) == 'L') {
direction = "east";
} else if (commands.charAt(i) == 'R') {
direction = "west";
} else {
System.out.println("Wrong command");
}
} else if (direction.equals("west")) {
if (commands.charAt(i) == 'G') {
x--;
} else if (commands.charAt(i) == 'L') {
direction = "south";
} else if (commands.charAt(i) == 'R') {
direction = "north";
} else {
System.out.println("Wrong command");
}
}
}
if (direction.equals("north") && (((x-initialX)*(x-initialX) + (y-initialY)*(y-initialY)) > 0)) {
return "NO";
} else {
return "YES";
}
}
}
Everything seems perfect, but I am not able to understand the condition:
if (direction.equals("north") && (((x-initialX)*(x-initialX) + (y-initialY)*(y-initialY)) > 0))
Can you please help me in understanding why we need to return NO for this case, what the formula && (((x-initialX)*(x-initialX) + (y-initialY)*(y-initialY)) > 0 indicates? and why the condition is checking only for direction "north" and not for other directions.
The last condition could be shortened to:
if (direction.equals("north") && (x != 0 || y != 0))
This condition means that after given sequence of steps robot has initial direction but not the initial position. In this case, this sequence shifts the position of the robot by (x, y). That means that after n repetitions of this sequence robot will have position (n*x, n*y). That is not bound by any radius when x != 0 || y != 0.
When both x and y are equal to zero, the robot has the initial direction and initial position after the given sequence of steps. That is a cycle and the answer is "Yes".
Otherwise, the direction of the robot has changed after the sequence of steps. There are two possibilities:
The direction has changed to the opposite (south). Let's assume that after the sequence of steps robot shifted to coordinates (x, y). But when we repeat this sequence in opposite direction coordinates will be changed by (-x, -y) and we will return to the (0, 0) coordinates with the initial direction.
The direction has changed to west or east. In this case, we will return to the initial position with the initial direction after four sequences of steps. For example, let's assume that direction was changed to east and position is (x, y) after the first sequence then:
Direction | Coordinates
-----------+--------------
north | (0, 0)
east | (x, y)
south | (x+y, y-x)
west | (y, -x)
north | (0, 0)

Calculating how far a robot will move in Java

I'm working on a project for school that requires me to move a robot. How far the robot will move each second (variable t) is calculated by the function below.
The first function is easy. The 2nd and 3rd on the other are where I'm stuck. How would I write F(t-1)? Below is what I have so far.
if (t == 0) {
distance = 2;
} else if (t > 0 && <=6 || t > 12) {
// No clue on how to write the 2nd distance equation.
} else if (t >= 7 && <=12) {
// No clue on how to write the 3rd distance equation.
}
Recursion really isn't necessary to solve this.
Note that in each of the non-zero time cases, F(t) = F(t-1) + something.
So you can simply do:
double f = 2; /* Initial value at t=0 */
for (int t = 1; t <= maxT; ++t) { // maxT is the maximum value of t.
if (t <= 6 || t > 12) {
f += /* something for case 2 */;
} else {
f += /* something for case 3 */;
}
}
System.out.println(f);
You can do this with recursion, but you will get a StackOverflowError if maxT becomes modestly large; by contrast, using a loop will work for arbitrarily large maxT (modulo floating point errors).
As pointed out by #Andreas, you can do this without looping over all values of t:
double f = 2 * (maxT + 1);
for (int t = 7; t <= maxT && t <= 12; ++t) {
f += log(t) - 2;
}
and you can eliminate that loop too by precomputing the values.
This is a problem which involves the use of recursion. By and large, pay close attention to the notation Ft-1, since that refers to an evaluation of the specific function at t-1.
I won't write out all of the code, but I'll give you some of the basics:
When t = 0, return 2. This is your base case.
When t is between 0 and 6 inclusive or greater than 12, return an evaluation of the function at t-1 and add 2.
When t is between 7 and 12 both inclusive, return an evaluation of the function at t-1 and add log2(t).
Here's something to get you at least started in the right direction.
public double evaluateDistance(int t) {
if(t == 0) {
return 2;
} else if(t > 0 && t <= 6) || (t > 12) {
// Think about this - it would involve another call to evaluateDistance, but what is t again?
} else if(t >= 7 && t <= 12) {
// Another evaluation involving the function.
// For free, the change of base operation you'll need to get base-2 evaluation for the log:
return ??? + Math.log(t)/Math.log(2);
}
}
Think I figured it out. Sorry if I wasn't clear on what I needed, just needed to figure out how to write the equations in the function. Think I figured it out though.
public double move()
{
int t = 0;
if(t == 0) // After the first second, robot moves 2
{
distance = 2;
}
else if(t > 0 && t <= 6 || t > 12) // From seconds 0 to 6 and after 12, robot moves distance equation
{
distance = (2*t)+2;
}
else if(t >= 7 && t <= 12) // From seconds 7 to 12, robot moves distances equation
{
distance = (2*t)+(Math.log(t)/Math.log(2));
}
position = position + distance;
return position;
}
}

codingBat xyzMiddle fails in other tests

This is my task:
Given a string, does "xyz" appear in the middle of the string? To define middle, we'll say that the number of chars to the left and right of the "xyz" must differ by at most one.
The problem description and the failures in others use case can be seen by using the code below here
xyzMiddle("AAxyzBB") → true
xyzMiddle("AxyzBB") → true
xyzMiddle("AxyzBBB") → false
My solution is below. Since I can't see what 'other tests' are, please help me spot the problem. My method is to check if 'y' appears in the middle for odd or even a String.
public boolean xyzMiddle(String str) {
if (str.indexOf("xyz") < 0) return false;
int l = str.length();
int m = l / 2;
if (l % 2 != 0) {
if (str.charAt(m) != 'y') return false;
}
else {
if (str.charAt(m) != 'y' && str.charAt(m - 1) != 'y') return false;
}
return true;
}
The problem with your solution is that you are simply returning false in case the string in question has odd length
Which is actually not true, the pass use-cases for this task can be mathematically divided like below:
1)With xyz present in the middle and there is a string of length x + 1 and x to either the left or right of it.
taking length of string xyz as 3 the total length comes out to be:
(x) + 3 + (x + 1) = 2x + 4 --->Always even
so in the case above we just check is xyz is in the middle or not and return accordingly which is already handled in your code.
2) With xyz present in the middle and there are strings of length x to the left or right of it.
Again taking length of string xyz as 3 the total length comes out to be:
(x) + 3 + (x) = 2x + 3 --->Always odd
Hence as per your solution to return true in this case(last line of code) you need to filter out the cases when length is odd but xyz is not in the middle as below:
if (!(str.substring((m - 1), m + 2).equals("xyz")))
return false;
With this included your solutions looks like as below:
public boolean xyzMiddle(String str) {
if (str.indexOf("xyz") < 0) return false;
int l = str.length();
int m = l / 2;
if (l % 2 != 0) {
if (!(str.substring((m - 1), m + 2).equals("xyz")))
return false;
}
else {
if (str.charAt(m) != 'y' && str.charAt(m - 1) != 'y') return false;
}
return true;
}
Now it passes all the tests on codingBat.
if(str.length() <3) return false;
int ind = str.indexOf("xyz", str.length()/2 - 3) ;
String first = str.substring(0, ind);
String second = str.substring(ind+3);
return (first.length() == second.length() || first.length() +1 == second.length() || first.length() == second.length() + 1);

Straight hand in Java poker game

I am having trouble finishing the straight method for a poker hand. I don't understand why my code doesn't work.
public static boolean containsStraight(int [] hand)
{
boolean straight = false;
for(int i = 0; i < 5; i++)
{
if (hand[i] == 2 && hand[i] == 3 && hand[i] == 4 && hand[i] == 5 && hand[i] == 6)
{
straight = true;
}
if (hand[i] == 3 && hand[i] == 4 && hand[i] == 5 && hand[i] == 6 && hand[i] == 7)
{
straight = true;
}
if (hand[i] == 4 && hand[i] == 5 && hand[i] == 6 && hand[i] == 7 && hand[i] == 8)
{
straight = true;
}
if (hand[i] == 5 && hand[i] == 6 && hand[i] == 7 && hand[i] == 8 && hand[i] == 9)
{
straight = true;
}
}
return straight;
}
As pL4Gu33 has already stated in his answer, your comparison is faulty. Essentially, each step through the for-loop leaves hand[i] at a constant value (say, 4). That means that your if-statements are checking:
if(4 == 4 && 4 == 5 && 4 == 6 && 4 == 7 && 4 == 8) {
...
}
This will never evaluate to true. If you knew for certain that you had five elements in the hand and that the hand was already sorted, you could do
if (hand[0] == 2 && hand[1] == 3 && hand[2] == 4 && hand[3] == 5 && hand[4] == 6) {
...
}
However, I'm going to show you a better answer.
The first thing you should do is to sort your hand. Once you do that, it's easy to step through the hand and check to see if the next card in the hand is exactly one greater than the previous card. If you get to the end and this holds true, then it's a straight.
/*
* You will need to import java.util.Arrays
*/
public boolean isStraight(int[] hand) {
if(hand == null || hand.length != 5) {
return false;
}
else {
// Automatically sort the hand
Arrays.sort(hand);
// Set the "previous" variable to a theoretically impossible value
int prev = -1;
// Iterate through the hand and see if the next card is exactly one more than
// the previous one.
for(int i = 0; i < hand.length; i++) {
// If prev is -1, then this is the first time through the for-loop
// If the card that we're on has a value of the previous card + 1,
// we still have the possibility of a straight.
if(prev == -1 || (prev + 1) == hand[i]) {
prev = hand[i];
}
else {
return false;
}
}
return true;
}
}
You say in every iteration hand[i] must be 2 AND 3 AND 4 AND 5. That is impossible. There is only one number in hand[i].
The problem is, that you are using the cycle incorrectly, because you are always checking the value of the same card in hand[i]. My suggestion would be either to do a sort first, or if you want to be more efficient, you can use a second field of booleans, that would indicate, whether a card of given value is present in your hand. This way you can easily check, if you have any number of cards in succesion.
public static boolean containsStraight(int[] cards) {
int count = 0;
boolean[] valueInHand = new boolean[10];
for (int card : cards) {
valueInHand[card] = true;
}
for (boolean value : valueInHand) {
if (value == true) {
count++;
} else {
count = 0;
}
// works for any number of cards
if (count == cards.length) {
return true;
}
}
return false;
}

Categories

Resources