I'm trying to create a method in a class for Java for a game called Quoridor in which a Pawn has to reach the other side of the board. The Pawn class (one coordinate) traverses a 9x9 2D array whereas the Wall classes (2 coordinates) are placed on a 10x10 2D array. The Walls are basically placed between the Pawn squares. Pawns cant cross Walls or other Pawns, I'm not sure how to implement the BFS with two 2D arrays. I'm new to programming and was wondering if someone could give me a step by step on how to create such a method. Currently have a Pawn and Wall class with necessary get and set methods.enter code here
package Players.HaydenLindquist;
import java.util.*;
import Engine.Logger;
import Interface.Coordinate;
import Interface.PlayerModule;
import Interface.PlayerMove;
public class HaydenLindquist implements PlayerModule {
Coordinate newCoords;
Wall theWall;
private Logger logOut;
Pawn player;
Pawn opponent;
List<Wall> wallList;
List<Pawn> pawnList;
public int getID()
{
return player.getId();
}
public Set<Coordinate> getNeighbors(Coordinate c) {
// Creates HashSet we will use to store neighbor tiles
Set<Coordinate> neighbor = new HashSet<Coordinate>();
int x = c.getRow();
int y = c.getCol();
// Coordinates for the 4 adjacent spaces
Coordinate top = new Coordinate(x,y-1);
Coordinate bottom = new Coordinate(x,y+1);
Coordinate left = new Coordinate(x-1,y);
Coordinate right = new Coordinate(x+1,y);
if(x == 0) {
if(y == 0) {
if(! wallCheck(right))
neighbor.add(right);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
else if(y == 8) {
if(! wallCheck(top))
neighbor.add(top);
if(! wallCheck(right))
neighbor.add(right);
}
else {
if(! wallCheck(top))
neighbor.add(top);
if(! wallCheck(right))
neighbor.add(right);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
}
else if(x == 8) {
if(y == 0) {
if(! wallCheck(left))
neighbor.add(left);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
else if(y == 8) {
if(! wallCheck(top))
neighbor.add(top);
if(! wallCheck(left))
neighbor.add(left);
}
else {
if(! wallCheck(top))
neighbor.add(top);
if(! wallCheck(left))
neighbor.add(left);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
}
else if(y == 0) {
if(! wallCheck(right))
neighbor.add(right);
if(! wallCheck(left))
neighbor.add(left);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
else if(y == 8) {
if(! wallCheck(right))
neighbor.add(right);
if(! wallCheck(left))
neighbor.add(left);
if(! wallCheck(top))
neighbor.add(top);
}
else {
if(! wallCheck(right))
neighbor.add(right);
if(! wallCheck(left))
neighbor.add(left);
if(! wallCheck(top))
neighbor.add(top);
if(! wallCheck(bottom))
neighbor.add(bottom);
}
return neighbor;
}
/**
*
*/
public Coordinate getPlayerLocation(int playerID)
{
if(playerID == player.getId())
{
return(player.getLocation());
}
else return(opponent.getLocation());
}
/**
*
*/
public Map<Integer, Coordinate> getPlayerLocations() {
// Creates HashMap of Integer, Coordinate type
HashMap<Integer, Coordinate> locations = new HashMap<Integer, Coordinate>();
// Adds the ID and locations of the 2 players to the HashMap
locations.put(player.getId(), player.getLocation());
locations.put(opponent.getId(), opponent.getLocation());
return locations;
}
/**
*
*/
public List<Coordinate> getShortestPath(Coordinate start, Coordinate end)
{
List<Coordinate> path = new ArrayList<Coordinate>();
return null;
}
/**
*
*/
public int getWallsRemaining(int playerID)
{
if(playerID == player.getId())
{
return(player.getWalls());
}
else return(opponent.getWalls());
}
/**
*
*/
public void init(Logger logger, int playerID, int numWalls, Map<Integer, Coordinate> playerHomes)
{
logOut = logger;
// Creates ArrayList used to store wall objects
wallList = new ArrayList<Wall>();
// Creates our two players and initializes them with data from engine
for ( Integer i : (Set<Integer>) playerHomes.keySet() )
{
if ( i == playerID )
player = new Pawn(playerID,numWalls,playerHomes.get(i));
else
{
opponent = new Pawn(2,numWalls,playerHomes.get(i));
}
}
}
public void lastMove(PlayerMove m)
{
// Check if m is a player move or wall placement
if(m.isMove())
{
// Switch to differentiate between player 1 and 2.
// then updates the appropriate players location
switch(m.getPlayerId())
{
case 1:
player.setLocation(m.getEnd());
break;
case 2:
opponent.setLocation(m.getEnd());
break;
}
}
else
{
switch(m.getPlayerId())
{
case 1:
addWall(m.getStart(), m.getEnd());
player.setWalls(player.getWalls() - 1);
break;
case 2:
addWall(m.getStart(), m.getEnd());
opponent.setWalls(player.getWalls() - 1);
break;
}
}
}
/**
*
*/
public Set<PlayerMove> allPossibleMoves()
{
return null;
}
/**
*
*/
public PlayerMove move()
{
return null;
}
/**
*
* #param player
* #return
*/
/**
*
*
*/
public void playerInvalidated(int playerID)
{
}
/**
* Method that creates a new wall object and adds it to the wallList ArrayList
*
* #param start
* #param end
*/
public void addWall(Coordinate start, Coordinate end)
{
Wall w = new Wall(start,end);
wallList.add(w);
}
/**
* A check method to see if entered coordinate contains a section of a wall
*
* #param c
* #return
*/
public boolean wallCheck(Coordinate c)
{
// Iterates through wall objects in wallList
for(int i = 0; i < wallList.size(); i++)
{
// Check if any adjacent squares contain a section of a wall
if(wallList.get(i).isWall(c))
{
return true;
}
}
return false;
}
}
Since you're starting with the idea of a BFS, and you've decided to represent your board with multidimensional arrays, why don't you start by thinking about how BFS maps to the representation of your board?
For example, can you write up the code to list all adjacent cells of a given cell? If you can do that, it should be easier to see how to implement the rest of BFS.
Related
I am trying to implement a depht first search alogrithm (my code is probably horrible, I'm sorry). Now I wanted to make this a recursive method but I just can't seem to be able to break out of it once the end condition is met. The first if-conditional you see in the method should break out of the method. When I was debugging the project it reached the return statement and then immediately jumped to the end of the method. But instead of stopping the whole thing it went back to the while(!allNeighboursVisited) loop and went on in an infinite loop.
I was trying to solve this by myself which did not work and started searching in the web, but I just could not find any solution to my problem.
EDIT: Decided to share the link to the project on my github for you guys to try it out: https://github.com/Equiphract/Maze
EDIT 2: Updated the code; I hacked it together so please don't expect anything that is pleasant to look at :)
Here is the recursive method:
public void depthFirstSearch(int x, int y, Tile[][] maze) {
// Return method after every Tile is visited.
if (this.visitedCounter == maze.length * maze[0].length) {
this.stack.clear();
return;
}
Tile currentTile = maze[x][y];
Random r = new Random();
int neighbourAmount = currentTile.getNeighbourAmount();
boolean allNeighboursVisited = false;
int stopCounter = 0;
// If it is a new Tile, mark it as visited
if (!currentTile.isVisited()) {
currentTile.setVisited(true);
this.visitedCounter++;
stack.add(currentTile);
}
// Check if neighbours are not yet visited and "visit" one of them.
while (!allNeighboursVisited) {
int random;
do {
random = r.nextInt(neighbourAmount);
} while (this.excludeList.contains(random));
Tile neighbour = currentTile.getNeighbours().get(random);
if (!neighbour.isVisited()) {
if (neighbour.getX() == currentTile.getX() - 1) {
currentTile.getWall(4).setOpen(true);
neighbour.getWall(2).setOpen(true);
} else if (neighbour.getX() == currentTile.getX() + 1) {
currentTile.getWall(2).setOpen(true);
neighbour.getWall(4).setOpen(true);
} else if (neighbour.getY() == currentTile.getY() - 1) {
currentTile.getWall(1).setOpen(true);
neighbour.getWall(3).setOpen(true);
} else if (neighbour.getY() == currentTile.getY() + 1) {
currentTile.getWall(3).setOpen(true);
neighbour.getWall(1).setOpen(true);
}
this.excludeList.clear();
depthFirstSearch(neighbour.getX(), neighbour.getY(), maze);
if (this.visitedCounter == maze.length * maze[0].length) {
this.stack.clear();
return;
}
} else {
this.excludeList.add(random);
stopCounter++;
}
if (stopCounter == neighbourAmount) {
allNeighboursVisited = true;
}
}
// If every neighbour has already been visited, go back one Tile.
if (!this.stack.isEmpty()) {
this.stack.remove(this.stack.size() - 1);
if (!this.stack.isEmpty()) {
Tile backtrackTile = this.stack.get(this.stack.size() - 1);
this.excludeList.clear();
depthFirstSearch(backtrackTile.getX(), backtrackTile.getY(), maze);
if (this.visitedCounter == maze.length * 3) {
this.stack.clear();
return;
}
}
this.excludeList.clear();
}
}
You know what, here is the Tile-Object (sorry for the high amount of edits in this short period):
public class Tile {
private ArrayList<Wall> walls;
private ArrayList<Tile> neighbours;
private int x;
private int y;
private boolean visited;
/*
* Constructor of the Tile class.
*/
public Tile(int x, int y) {
this.walls = new ArrayList<Wall>();
this.neighbours = new ArrayList<Tile>();
this.walls.add(new Wall(1));
this.walls.add(new Wall(2));
this.walls.add(new Wall(3));
this.walls.add(new Wall(4));
this.x = x;
this.y = y;
this.visited = false;
}
/*
* Returns the ArrayList walls.
*/
public ArrayList<Wall> getWalls() {
return walls;
}
/*
* Returns the value of visited.
*/
public boolean isVisited() {
return visited;
}
/*
* Sets the value of visited to a specified value.
*
* #param visited a boolean value
*/
public void setVisited(boolean visited) {
this.visited = visited;
}
/*
* Returns a wall with the specified position.
*
* #param position the position of the wall
*/
public Wall getWall(int position) {
for(Wall w : this.walls) {
if(w.getPosition() == position) {
return w;
}
}
return null;
}
public int getNeighbourAmount() {
return this.neighbours.size();
}
public ArrayList<Tile> getNeighbours(){
return this.neighbours;
}
/*
* Adds a Tile to the ArrayList neighbours-
*
* #param t a Tile
*/
public void addNeighbour(Tile t) {
this.neighbours.add(t);
}
/**
* #return the x
*/
public int getX() {
return x;
}
/**
* #return the y
*/
public int getY() {
return y;
}
}
Ok I think I found a solution to my question. It is far from perfect and needs a lot of optimisation, maybe one of you guys want to do that and post it here^^.
My main mistake was not adding returns after each time I invoked the method recursively, which resulted in an endless-loop.
Here is my solution:
public void depthFirstSearch(int x, int y, Tile[][] maze) {
// Return method after every Tile is visited.
if (this.visitedCounter == maze.length * maze[0].length) {
this.stack.clear();
return;
}
Tile currentTile = maze[x][y];
Random r = new Random();
int neighbourAmount = currentTile.getNeighbourAmount();
boolean allNeighboursVisited = false;
int stopCounter = 0;
// If it is a new Tile, mark it as visited
if (!currentTile.isVisited()) {
currentTile.setVisited(true);
this.visitedCounter++;
stack.add(currentTile);
}
// Check if neighbours are not yet visited and "visit" one of them.
while (!allNeighboursVisited) {
int random;
do {
random = r.nextInt(neighbourAmount);
} while (this.excludeList.contains(random));
Tile neighbour = currentTile.getNeighbours().get(random);
if (!neighbour.isVisited()) {
if (neighbour.getX() == currentTile.getX() - 1) {
currentTile.getWall(4).setOpen(true);
neighbour.getWall(2).setOpen(true);
} else if (neighbour.getX() == currentTile.getX() + 1) {
currentTile.getWall(2).setOpen(true);
neighbour.getWall(4).setOpen(true);
} else if (neighbour.getY() == currentTile.getY() - 1) {
currentTile.getWall(1).setOpen(true);
neighbour.getWall(3).setOpen(true);
} else if (neighbour.getY() == currentTile.getY() + 1) {
currentTile.getWall(3).setOpen(true);
neighbour.getWall(1).setOpen(true);
}
this.excludeList.clear();
depthFirstSearch(neighbour.getX(), neighbour.getY(), maze);
return;
} else {
this.excludeList.add(random);
stopCounter++;
}
if (stopCounter == neighbourAmount) {
allNeighboursVisited = true;
}
}
// If every neighbour has already been visited, go back one Tile.
if (!this.stack.isEmpty()) {
this.stack.remove(this.stack.size() - 1);
if (!this.stack.isEmpty()) {
Tile backtrackTile = this.stack.get(this.stack.size() - 1);
this.excludeList.clear();
depthFirstSearch(backtrackTile.getX(), backtrackTile.getY(), maze);
return;
}
this.excludeList.clear();
}
}
I am tasked with creating a postFixEvaluator in java and everytime i try to pop an operand using the evaluateTwoOperations method, my program ends and produces the below error. What is causing this? to my knowledge, I have everything typecasted to be a Complex and not a Integer.
"Couldn't run PostfixEvaluator! java.lang.ClassCastException: java.lang.Integer cannot be cast to Complex"
Start of my postFixEvaluator class
#author Cody
*/
import java.util.*;
import java.io.*;
import java.util.StringTokenizer;
public class PostfixEvaluator
{
private static final int STACK_SIZE = 100;
private Stack operand;
private String expression;
private ArrayList<Complex> answers;
public static Complex result;
public void run() throws IOException
{
ArrayList<Complex> a = new ArrayList<>();
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
//BufferedReader stdin = new BufferedReader(new FileReader("Prog3_1.in"));
operand = new Stack(STACK_SIZE);
boolean eof = false;
while (!eof) //while not done
{
expression = stdin.readLine(); //read a line of expression
//operand.clear(); //clear the stack
if (expression == null) //if no input, end program
{
eof = true;
}
else
{
result = evaluate(expression); //evaluate expression
}
if (result != null) //if the answer is valid
{
System.out.println("\nvalue: " + result.toString());
answers.add(result);//add answer to arrayList
}
else
{
System.out.println("\nInvalid Expression");
}
}
//read the ArrayList and print the results
/*
if (real)
/
for (int i = 0; i < answers.size(); i++)
{
System.out.println(answers.get(i));
}
*/
System.out.println("Normal termination of"
+ "program 3");
}
public Complex evaluate(String expression)
{
boolean valid = true;
StringTokenizer st = new StringTokenizer(expression); //tokenize the expression
String token; //get individual results of readLine
while (valid && st.hasMoreTokens()) //while valid and their are tokens remaining
{
token = st.nextToken(); //get next token
System.out.println("token is: " + token);
if (isOperator(token)) //if token is operator
{
if (isConjugate(token))
{// if token is conjugate
Complex result1 = evaluateConjugate(token);
operand.push(result1);
}
else //evaluate the operator and push the result
{
Complex result1 = evaluateTwoOperations(token);
operand.push(result1);
}
}
else
{
int value = Integer.parseInt(token);
operand.push(value);
}
}
Complex answer = (Complex) operand.pop();
return answer;
}
private Complex evaluateTwoOperations(String op)
{
Complex resultant = null;
if ( operand.isEmpty() )
System.out.println("Invalid Expression!");
else
{
Complex op2 = (Complex) operand.pop();
Complex op1 = (Complex) operand.pop();
switch (op.charAt(0)) //if op == '+'...
{
case '+':
resultant = op1.plus(op2);
break;
case '-':
resultant = op1.minus(op2);
case '*':
resultant = op1.times(op2);
}
return resultant;
}
return null;
}
private boolean isOperator(String token)
{
return token.equals("+") || token.equals("-")
|| token.equals("*") || token.equals("~");
}
private boolean isConjugate(String token)
{
return token.equals("~");
}
private Complex evaluateConjugate(String op)
{
Complex resultant = null;
if (operand.isEmpty())
System.out.println("Invalid Expression!");
else
{
Complex op1 = (Complex) operand.pop();
switch (op.charAt(0)) //if op == '~'
{
case '~':
resultant = op1.conjugate();
}
return resultant;
}
return null;
}
/*
private void outputExpression(String expression)
{
for (int i = 1; i < answers.size(); i++)
{
System.out.println("Expression " + i + ": " + expression);
}
}
*/
}
Start of my Complex class
#author Cody
*/
public class Complex
{
private int realNumber;
private int imagNumber;
private final int checker = 0;
/**
default constructor creates the default complex number a+bi where a=b=0.
*/
public Complex()
{
realNumber = checker;
imagNumber = checker;
}
/**
parameterized constructor creates the complex number a+bi, where b=0.
*/
public Complex(int real)
{
realNumber = real;
imagNumber = checker;
}
/**
parameterized constructor creates the complex number a+bi where a and b are
integers.
*/
public Complex(int real, int imag)
{
realNumber = real;
imagNumber = imag;
}
/**
method returns a new Complex number that represents the sum
of two Complex numbers.
#param cp
#return new summed complex number
*/
public Complex plus(Complex cp)
{
return new Complex(realNumber + cp.realNumber, imagNumber + cp.imagNumber);
}
/**
method returns a new Complex that represents the difference
between two Complex numbers
#param cp
#return difference of two Complex numbers
*/
public Complex minus(Complex cp)
{
return new Complex(realNumber - cp.realNumber, imagNumber - cp.imagNumber);
}
/**
method returns a new Complex that represents the product of
two Complex numbers
#param cp
#return product of two complex numbers
*/
public Complex times(Complex cp)
{
return new Complex(realNumber * cp.realNumber - imagNumber * cp.imagNumber,
realNumber * cp.realNumber + imagNumber * cp.realNumber);
}
/**
method should return a new Complex that is the conjugate of a
Complex number
#param none
#return conjugate of complex number
*/
public Complex conjugate()
{
return new Complex(realNumber, -imagNumber);
}
/**
method returns true if two Complex numbers have the same
real and imaginary; return false otherwise.
#param obj
#return true if two complex numbers are equal, false otherwise
*/
public boolean equals(Object obj)
{
if (obj instanceof Complex)
{
Complex n = (Complex) obj;
return n.realNumber == realNumber && n.imagNumber == imagNumber;
}
return false;
}
/**
method returns a complex number as a String.
uses multiple if statements to check validity
#param none
#return complex number as a string
*/
public String toString()
{
if (imagNumber == checker)
{
return String.valueOf(realNumber);
}
if (realNumber == checker && imagNumber == checker)
{
return String.valueOf(checker);
}
if (realNumber == checker)
{
return imagNumber + "i";
}
if (realNumber != checker && imagNumber < checker)
{
return realNumber + " - " + -imagNumber + "i";
}
if (realNumber != checker && imagNumber > checker)
{
return realNumber + " + " + imagNumber + "i";
}
return "invalid";
}
}
Start of my Stack class
/**
performs all practical Stack operations
#author Cody
*/
public class Stack
{
private Object[] elements;
private int top;
/**
parameterized constructor creates array of Elements with size of size
initializes top to 0
#param size
*/
public Stack(int size)
{
elements = new Object[size];
top = 0;
}
/**
checks to see if the array is empty
#param none
#return true if array is empty, false otherwise.
*/
public boolean isEmpty()
{
return top == 0;
}
/**
checks to see if the array is full
#param none
#return true if array is full, false otherwise.
*/
public boolean isFull()
{
return top == elements.length;
}
/**
returns the item most recently added to the stack. if the array is empty,
returns null
#param none
#return last item added to stack, null if empty array
*/
public Object peek()
{
if (top == 0)
return null;
return elements[top - 1];
}
/**
returns and deletes element most recently added to the stack
#param none
#return element most recently added to stack
*/
public Object pop()
{
return elements[--top];
}
/**
adds item to stack and increments top
#param obj
*/
public void push(Object obj)
{
elements[top++] = obj;
}
/**
returns number of items in stack
#param none
#return number of items in stack
*/
public int size()
{
return elements.length;
}
/**
clears the stack by popping everything in it
#param none
*/
public void clear()
{
for (int i = 0; i < size(); i++)
elements[i] = pop();
}
}
In your evaluate method, you have this piece of code:
int value = Integer.parseInt(token);
operand.push(value);
But when you pop such values , you are converting it to (Complex) in your evaluateTwoOperations method. Hence, you are getting the ClassCastException.
You already have a constructor to help you convert your Integer to Complex class
You could use that.
public Complex(int real)
{
realNumber = real;
imagNumber = checker;
}
I'm hoping to find a bit of direction for this problem I was given. Been banging my head over it for two weeks now. Essentially, I'm to write a function, public static int FindWords(char[][] puzzle) , where given a 2D char array, I can find the amount of times a given set of strings occur. Given:
public static class PuzzleSolver
{
public static string[] DICTIONARY = {"OX","CAT","TOY","AT","DOG","CATAPULT","T"};
static bool IsWord(string testWord)
{
if (DICTIONARY.Contains(testWord))
return true;
return false;
}
}
A 2D Array for instance that is like this:
public static char[][] puzzle = {{'C','A','T'},
{'X','Z','T'},
{'Y','O','T'}};
Would return back 8 for the following instances: (AT, AT, CAT, OX, TOY, T, T, T) because we would be searching horizontally, vertically, diagonally and in reverse for all the same directions.
My approach was to visit each char in the array and then search for all possible directions with the SearchChar function...
public static int FindWords(char[][] puzzle){
int arrayRow = puzzle.length;
int arrayCol = puzzle[0].length;
int found = 0;
for(int i = 0; i < arrayRow; i++){
for(int j = 0; j < arrayCol; j++){
found += SearchChar(i,j);
}
}
return found;
}
Which looks like this:
public static int SearchChar(int row, int col){
if(row < 0 || col < 0 || row > puzzle.length || col > puzzle[0].length)//Is row or col out of bounds?
return 0;
if(IsWord(puzzle[row][col]))
return 1;
return 0;
}
Conceptually, I feel like I need some kind of recursive function to handle this but I can't seem to wrap my head around it. I don't even know if that's the right approach. I've been playing around with StringBuilder appending but I'm still struggling with that too. I think this recursive function would need to take for instance, puzzle[0][0] (or 'C') and see if it is in the dictionary. Followed by puzzle[0][0] + puzzle[0][1] (or 'CA') and then finally puzzle[0][0] + puzzle[0][1] + puzzle[0][2] (or 'CAT'). Then the same would have to be don vertically and diagonally. I'm having trouble trying to get back into the SearchChar function with a position change to append to the original char so that I can see if it is in the DICTIONARY.
Sorry if this is a bit wordy, but I just want to give the impression that I'm actually trying to solve this. Not just some lazy programmer that's copy & pasting some problem up here for someone else to solve. Thanks in advance for any help!
I will show you how to solve this problem step by step.
1. Generating All Possible Words from the given Puzzle
to do this we must start anywhere in the puzzle and move towards all directions (except the previous Point) to generate all possible words;
2. Choosing Suitable Data Structure for Dictionary
I think Trie is a good choice and is suitable for use in such situations.
The most important reason for choosing Trie is that during the search, we can easily test if a word exists in our dictionary or is there any word that starts with the word generated by searching through the puzzle or not.
As a result, we can decide whether or not to continue the search.
This will save us a lot of time and helps to generate words correctly.
otherwise, we'll be stuck in an endless loop...
3. Implementation
there are several implementations for Tire , but I wrote my own CharTrie :
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* #author FaNaJ
*/
public final class CharTrie {
/**
* Pointer to root Node
*/
private final Node root = new Node();
/**
* Puts the specified word in this CharTrie and increases its frequency.
*
* #param word word to put in this CharTrie
* #return the previous frequency of the specified word
*/
public int put(String word) {
if (word.isEmpty()) {
return 0;
}
Node current = root;
for (int i = 0; i < word.length(); i++) {
current = current.getChildren().computeIfAbsent(word.charAt(i), ch -> new Node());
}
return current.getAndIncreaseFrequency();
}
/**
* #param word the word whose frequency is to be returned
* #return the current frequency of the specified word or -1 if there isn't such a word in this CharTrie
*/
public int frequency(String word) {
if (word.isEmpty()) {
return 0;
}
Node current = root;
for (int i = 0; i < word.length() && current != null; i++) {
current = current.getChildren().get(word.charAt(i));
}
return current == null ? -1 : current.frequency;
}
/**
* #param word the word whose presence in this CharTrie is to be tested
* #return true if this CharTrie contains the specified word
*/
public boolean contains(String word) {
return frequency(word) > 0;
}
/**
* #return a CharTrie Iterator over the Nodes in this CharTrie, starting at the root Node.
*/
public Iterator iterator() {
return new Iterator(root);
}
/**
* Node in the CharTrie.
* frequency-children entry
*/
private static final class Node {
/**
* the number of occurrences of the character that is associated to this Node,
* at certain position in the CharTrie
*/
private volatile int frequency = 0;
private static final AtomicIntegerFieldUpdater<Node> frequencyUpdater
= AtomicIntegerFieldUpdater.newUpdater(Node.class, "frequency");
/**
* the children of this Node
*/
private Map<Character, Node> children;
public Map<Character, Node> getChildren() {
if (children == null) {
children = new ConcurrentHashMap<>();
}
return children;
}
/**
* Atomically increments by one the current value of the frequency.
*
* #return the previous frequency
*/
private int getAndIncreaseFrequency() {
return frequencyUpdater.getAndIncrement(this);
}
}
/**
* Iterator over the Nodes in the CharTrie
*/
public static final class Iterator implements Cloneable {
/**
* Pointer to current Node
*/
private Node current;
private Iterator(Node current) {
this.current = current;
}
/**
* Returns true if the current Node contains the specified character in its children,
* then moves to the child Node.
* Otherwise, the current Node will not change.
*
* #param ch the character whose presence in the current Node's children is to be tested
* #return true if the current Node's children contains the specified character
*/
public boolean next(char ch) {
Node next = current.getChildren().get(ch);
if (next == null) {
return false;
}
current = next;
return true;
}
/**
* #return the current frequency of the current Node
*/
public int frequency() {
return current.frequency;
}
/**
* #return the newly created CharTrie Iterator, starting at the current Node of this Iterator
*/
#Override
#SuppressWarnings("CloneDoesntCallSuperClone")
public Iterator clone() {
return new Iterator(current);
}
}
}
and the WordGenerator :
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.function.BiConsumer;
/**
* #author FaNaJ
*/
public final class WordGenerator {
private WordGenerator() {
}
public static void generate(char[][] table, CharTrie.Iterator iterator, BiConsumer<String, Integer> action) {
final ForkJoinPool pool = ForkJoinPool.commonPool();
final VisitorContext ctx = new VisitorContext(table, action);
for (int y = 0; y < table.length; y++) {
for (int x = 0; x < table[y].length; x++) {
pool.invoke(new Visitor(new Point(x, y), null, "", iterator.clone(), ctx));
}
}
}
private static final class VisitorContext {
private final char[][] table;
private final BiConsumer<String, Integer> action;
private VisitorContext(char[][] table, BiConsumer<String, Integer> action) {
this.table = table;
this.action = action;
}
private boolean validate(Point point) {
Object c = null;
try {
c = table[point.getY()][point.getX()];
} catch (ArrayIndexOutOfBoundsException ignored) {
}
return c != null;
}
}
private static final class Visitor extends RecursiveAction {
private final Point current;
private final Point previous;
private final CharTrie.Iterator iterator;
private final VisitorContext ctx;
private String word;
private Visitor(Point current, Point previous, String word, CharTrie.Iterator iterator, VisitorContext ctx) {
this.current = current;
this.previous = previous;
this.word = word;
this.iterator = iterator;
this.ctx = ctx;
}
#Override
protected void compute() {
char nextChar = ctx.table[current.getY()][current.getX()];
if (iterator.next(nextChar)) {
word += nextChar;
int frequency = iterator.frequency();
if (frequency > 0) {
ctx.action.accept(word, frequency);
}
List<Visitor> tasks = new ArrayList<>();
for (Direction direction : Direction.values()) {
Point nextPoint = direction.move(current);
if (!nextPoint.equals(previous) && ctx.validate(nextPoint)) {
tasks.add(new Visitor(nextPoint, current, word, iterator.clone(), ctx));
}
}
invokeAll(tasks);
}
}
}
}
Note that I've used ForkJoinPool and RecursiveAction to speed up the search.
learn more :
https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
http://tutorials.jenkov.com/java-util-concurrent/java-fork-and-join-forkjoinpool.html
http://www.javaworld.com/article/2078440/enterprise-java/java-tip-when-to-use-forkjoinpool-vs-executorservice.html
the rest of classes :
PuzzleSolver
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
/**
* #author FaNaJ
*/
public final class PuzzleSolver {
private final CharTrie dictionary;
public enum OrderBy {FREQUENCY_IN_DICTIONARY, FREQUENCY_IN_PUZZLE}
public PuzzleSolver(CharTrie dictionary) {
this.dictionary = dictionary;
}
public CharTrie getDictionary() {
return dictionary;
}
public Stream<Word> solve(char[][] puzzle) {
return solve(puzzle, OrderBy.FREQUENCY_IN_DICTIONARY);
}
public Stream<Word> solve(char[][] puzzle, OrderBy orderBy) {
Stream<Word> stream = null;
switch (orderBy) {
case FREQUENCY_IN_DICTIONARY: {
final Map<String, Integer> words = new ConcurrentHashMap<>();
WordGenerator.generate(puzzle, dictionary.iterator(), words::put);
stream = words.entrySet().stream()
.map(e -> new Word(e.getKey(), e.getValue()));
break;
}
case FREQUENCY_IN_PUZZLE: {
final Map<String, AtomicInteger> words = new ConcurrentHashMap<>();
BiConsumer<String, Integer> action = (word, frequency) -> words.computeIfAbsent(word, s -> new AtomicInteger()).getAndIncrement();
WordGenerator.generate(puzzle, dictionary.iterator(), action);
stream = words.entrySet().stream()
.map(e -> new Word(e.getKey(), e.getValue().get()));
break;
}
}
return stream.sorted((a, b) -> b.compareTo(a));
}
}
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
Point
import java.util.Objects;
/**
* #author FaNaJ
*/
public final class Point {
private final int x;
private final int y;
public Point() {
this(0, 0);
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public int hashCode() {
return x * 31 + y;
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Point that = (Point) obj;
return x == that.x && y == that.y;
}
#Override
public String toString() {
return "[" + x + ", " + y + ']';
}
}
Word
/**
* #author FaNaJ
*/
public final class Word implements Comparable<Word> {
private final String value;
private final int frequency;
public Word(String value, int frequency) {
this.value = value;
this.frequency = frequency;
}
public String getValue() {
return value;
}
public int getFrequency() {
return frequency;
}
#Override
public int hashCode() {
return value.hashCode() * 31 + frequency;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Word that = (Word) o;
return frequency == that.frequency && value.equals(that.value);
}
#Override
public String toString() {
return "{" +
"value='" + value + '\'' +
", frequency=" + frequency +
'}';
}
#Override
public int compareTo(Word o) {
return Integer.compare(frequency, o.frequency);
}
}
Direction
/**
* #author FaNaJ
*/
public enum Direction {
UP(0, 1), UP_RIGHT(1, 1), UP_LEFT(-1, 1),
RIGHT(1, 0), LEFT(-1, 0),
DOWN(0, -1), DOWN_RIGHT(1, -1), DOWN_LEFT(-1, -1);
private final int x, y;
Direction(int x, int y) {
this.x = x;
this.y = y;
}
public Point move(Point point) {
return new Point(point.getX() + x, point.getY() + y);
}
}
4. Test it
/**
* #author FaNaJ
*/
public class Test {
public static String[] DICTIONARY = {"OX", "CAT", "TOY", "AT", "DOG", "CATAPULT", "T", "AZOYZACZOTACXY"};
public static void main(String[] args) {
CharTrie trie = new CharTrie();
for (String word : DICTIONARY) {
trie.put(word);
}
PuzzleSolver solver = new PuzzleSolver(trie);
char[][] puzzle = {
{'C', 'A', 'T'},
{'X', 'Z', 'T'},
{'Y', 'O', 'T'}
};
solver.solve(puzzle, PuzzleSolver.OrderBy.FREQUENCY_IN_PUZZLE).forEach(System.out::println);
}
}
output :
{value='T', frequency=3}
{value='AT', frequency=2}
{value='CAT', frequency=2}
{value='TOY', frequency=2}
{value='OX', frequency=1}
{value='AZOYZACZOTACXY', frequency=1}
I've implemented the following MiniMax algorithm for my Android Reversi game:
#Override
public Field findBestMove(GameBoard gb, int depth, boolean player)
{
/** maximum depth of search reached, we stop */
if(depth >= max_depth) return null;
//player = (depth+1)%2 + 1;
/** getting a list of moves to chose from */
ArrayList <Field> moves = findAllPossibleMoves(gb, player);
Field best_move = null;
/** iterating over all possible moves, to find the best one */
for (int i=0; i<moves.size(); i++)
{
/** board to simulate moves */
GameBoard temp_board = new GameBoard(gb);
/** getting the current move */
Field move = moves.get(i);
/** simulating the move for the current node */
game.move(move, temp_board, player);
Log.i("board", "Depth:"+depth+" Player:"+player+" Move:"+i+" Rating:"+evaluate(temp_board));
Log.i("board", ""+moves.get(i).getX()+","+moves.get(i).getY());
temp_board.printBoard();
/** getting to the next inferior node */
Field best_deep_move = findBestMove (temp_board, depth + 1, !player);
/** if the maximum depth is reached, we have a null, so we evaluate */
if (best_deep_move == null)
{
move.setRating(evaluate (temp_board));
}
/** if we are not the deepest possible, we get the rating from the lower node */
else
{
move.setRating(best_deep_move.getRating());
Log.i("eval", ""+best_deep_move.getRating());
}
if(best_move == null)
{
best_move = move;
}
else
{
Log.i("update", "Current move rating:"+move.getRating());
Log.i("update", "New move rating:"+best_move.getRating());
if (depth%2==0)
{
Log.i("update", "MAX player");
/** for us, we look for the maximum */
if (best_move.getRating() < move.getRating())
{
best_move = move;
}
}
else
{
Log.i("update", "MIN player");
/** for the opponent, we look for the minimum */
if (best_move.getRating() > move.getRating())
{
best_move = move;
}
}
Log.i("update", "Updated move rating"+best_move.getRating());
}
}
return best_move;
}
I've made myself familiar with the Alpha-Beta pruning in theory, though I'm having some trouble proceeding with applying that knowledge in this algorithm. Thanks in advance
There following changes that need to done to your code to implement alpha-beta pruning:-
pass a parameter public Field findBestMove(GameBoard gb, int depth, boolean player,int aplha_beta)
Stop recursion if current best_move will never affect alpha_beta of previous depth.
if(player == max && best_move!=null && aplha_beta <= best_move.getRating()) {
return(best_move);
}
if(player == min && best_move!=null && alpha_beta >= best_move.getRating()) {
return(best_move);
}
Field best_deep_move = findBestMove(temp_board,depth+1,!player,best_move.getRating());
I need to find if Path2D intersects itself. For now, I do it by simply extracting an array of lines from path, and finding if any of these intersect. But it has O(n^2) complexity, and so it is very slow. Is there a faster way to do it?
You can do this faster using the sweep-line algorithm: http://en.wikipedia.org/wiki/Sweep_line_algorithm
Pseudocode:
Each line has a start point and an end point. Say that `start_x` <= `end_x` for all the lines.
Create an empty bucket of lines.
Sort all the points by their x coordinates, and then iterate through the sorted list.
If the current point is a start point, test its line against all the lines in the bucket, and then add its line to the
bucket.
if the current point is an end point, remove its line from the bucket.
The worst case is still O(N^2), but the average case is O(NlogN)
Here is my Java implementation of this algorithm:
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.util.*;
/**
* Path2D helper functions.
* <p/>
* #author Gili Tzabari
*/
public class Path2Ds
{
/**
* Indicates if a Path2D intersects itself.
* <p/>
* #return true if a Path2D intersects itself
*/
public static boolean isSelfIntersecting(PathIterator path)
{
SortedSet<Line2D> lines = getLines(path);
if (lines.size() <= 1)
return false;
Set<Line2D> candidates = new HashSet<Line2D>();
for (Line2D line: lines)
{
if (Double.compare(line.getP1().distance(line.getP2()), 0) <= 0)
{
// Lines of length 0 do not cause self-intersection
continue;
}
for (Iterator<Line2D> i = candidates.iterator(); i.hasNext();)
{
Line2D candidate = i.next();
// Logic borrowed from Line2D.intersectsLine()
int lineRelativeToCandidate1 = Line2D.relativeCCW(line.getX1(), line.getY1(), line.
getX2(),
line.getY2(), candidate.getX1(), candidate.getY1());
int lineRelativeToCandidate2 = Line2D.relativeCCW(line.getX1(), line.getY1(), line.
getX2(),
line.getY2(), candidate.getX2(), candidate.getY2());
int candidateRelativeToLine1 = Line2D.relativeCCW(candidate.getX1(),
candidate.getY1(),
candidate.getX2(), candidate.getY2(), line.getX1(), line.getY1());
int candidateRelativeToLine2 = Line2D.relativeCCW(candidate.getX1(),
candidate.getY1(),
candidate.getX2(), candidate.getY2(), line.getX2(), line.getY2());
boolean intersection = (lineRelativeToCandidate1 * lineRelativeToCandidate2 <= 0)
&& (candidateRelativeToLine1 * candidateRelativeToLine2 <= 0);
if (intersection)
{
// Lines may share a point, so long as they extend in different directions
if (lineRelativeToCandidate1 == 0 && lineRelativeToCandidate2 != 0)
{
// candidate.P1 shares a point with line
if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0)
{
// line.P1 == candidate.P1
continue;
}
if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0)
{
// line.P2 == candidate.P1
continue;
}
// else candidate.P1 intersects line
}
else if (lineRelativeToCandidate1 != 0 && lineRelativeToCandidate2 == 0)
{
// candidate.P2 shares a point with line
if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0)
{
// line.P1 == candidate.P2
continue;
}
if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0)
{
// line.P2 == candidate.P2
continue;
}
// else candidate.P2 intersects line
}
else
{
// line and candidate overlap
}
return true;
}
if (candidate.getX2() < line.getX1())
i.remove();
}
candidates.add(line);
}
return false;
}
/**
* Returns all lines in a path. The lines are constructed such that the starting point is found
* on the left (or same x-coordinate) of the ending point.
* <p/>
* #param path the path
* #return the lines, sorted in ascending order of the x-coordinate of the starting point and
* ending point, respectively
*/
private static SortedSet<Line2D> getLines(PathIterator path)
{
double[] coords = new double[6];
SortedSet<Line2D> result = new TreeSet<Line2D>(new Comparator<Line2D>()
{
#Override
public int compare(Line2D o1, Line2D o2)
{
int result = Double.compare(o1.getX1(), o2.getX1());
if (result == 0)
{
// Ensure we are consistent with equals()
return Double.compare(o1.getX2(), o2.getX2());
}
return result;
}
});
if (path.isDone())
return result;
int type = path.currentSegment(coords);
assert (type == PathIterator.SEG_MOVETO): type;
Point.Double startPoint = new Point.Double(coords[0], coords[1]);
Point.Double openPoint = startPoint;
path.next();
while (!path.isDone())
{
type = path.currentSegment(coords);
assert (type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO): type;
switch (type)
{
case PathIterator.SEG_MOVETO:
{
openPoint = startPoint;
break;
}
case PathIterator.SEG_CLOSE:
{
coords[0] = openPoint.x;
coords[1] = openPoint.y;
break;
}
}
Point.Double endPoint = new Point.Double(coords[0], coords[1]);
if (Double.compare(startPoint.getX(), endPoint.getX()) < 0)
result.add(new Line2D.Double(startPoint, endPoint));
else
result.add(new Line2D.Double(endPoint, startPoint));
path.next();
startPoint = endPoint;
}
return result;
}
}