I have the following portion of ugly (but working) code for checking if fields on a chessboard are vacant:
if (abs(xDst - xSrc) == abs(yDst - ySrc)) {
boolean backslashMove = xSrc < xDst && ySrc > yDst || xSrc > xDst && ySrc < yDst;
if (backslashMove) {
int y = max(ySrc, yDst) - 1;
for (int x = min(xSrc, xDst) + 1; x < max(xSrc, xDst); x++) {
if (board.getActiveChessmanAt(x, y).isAlive()) {
return false;
}
y--;
}
} else { //slash move
Obviously, it examines fields between coordinates (xScr, ySrc) and (xDst, yDst) in Bishop-like line of move.
I'm trying to transform this with using IntStream:
if (backslashMove) {
final int y = max(ySrc, yDst) - 1;
if (IntStream.range(min(xSrc, xDst) + 1, max(xSrc, xDst))
.anyMatch(x -> board.getActiveChessmanAt(x, y).isAlive()))
return false;
How can I perform y-- in this case? It has to be final if it's about to be used within 'anyMatch' command
If you really need to rewrite it using streams, then you can use the fact that both x and y are incremented simultaneously. So you can build a range of increments instead of the range of x-values:
final int xSrc = min(xSrc, xDst) + 1;
final int xDst = max(xSrc, xDst);
final int ySrc = max(ySrc, yDst) - 1;
if (IntStream.range(0, xDst - xSrc)
.anyMatch(distance -> board.getActiveChessmanAt(xSrc + distance, ySrc + distance).isAlive())) {
return false;
}
In general, it's not possible to use a non-final local variable from the "parent" method directly. Java doesn't support real closures. You would need a wrapper object for this (AtomicInteger is an often suggested candidate), or you could make the non-final variable a class field (note the potential thread safety problems). To me personally, these both "tricks" are bad.
What you need is not functional programming in terms of streams/folds.
Instead of, you should refactor your actual code to make it clearer/shorter/better.
You could for example :
extract the parts of logic scattered in the actual method in specific methods with meaningful names
use structured objects rather than too fine unitary variable
remove undesirable nesting : use early exit and not required conditional statements may help
It could give :
// -> extract method + structured objects
if (!isPointsMatch(pointSrc, pointDst)) {
return false; // -> early exit
}
// -> extract method + structured objects
if (isBackslashMove(pointSrc, pointDst)) {
if (board.hasAnyActiveChessmanAlive(pointSrc, pointDst)) {
return false;
}
}
// else slash move -> The else is useless
// ...
Your original code snipped is procedural. Your functional approach does not work well. So how about an object oriented approach?
class Position{
private final int x, y;
public Position(int x, int y){
this.x=x;
this.y=y;
}
public getX(){
return x;
}
public getY(){
return y;
}
}
interface Move {
Position moveFrom(Position start);
}
interface Figure {
Collection<Move> getPossibleMoves(Position start, Board board);
}
class Bishop implements Figure {
private final Collection<Move> moves = new HashSet<>();
public Bishop(){
moves.add(start->new Position(start.getX()-2,start.getY()-1));
moves.add(start->new Position(start.getX()-2,start.getY()+1));
moves.add(start->new Position(start.getX()+2,start.getY()-1));
moves.add(start->new Position(start.getX()+2,start.getY()+1));
moves.add(start->new Position(start.getX()-1,start.getY()-2));
moves.add(start->new Position(start.getX()-1,start.getY()+2));
moves.add(start->new Position(start.getX()+1,start.getY()-2));
moves.add(start->new Position(start.getX()+1,start.getY()+2));
}
#Override
public Collection<Move> getPossibleMoves(Position start, Board board){
return moves.stream()
.filter({ Position end = m.moveFrom(start);
return board.isOnBorad(end.getX(),end.getY())
&& board.getActiveChessmanAt(end.getX(), end.getY()).isAlive()})
.collect(Collectors.toSet());
}
}
Another implementation of Figure might return a separate Move instance for each step until it reaches a limit:
class Tower implements Figure {
enum Direction {
NORTH(1,0),EAST(0,1),SOUTH(-1,0),WEST(0,-1);
private final Position change;
private Direction(int x, int y){
change = new Position(x, y);
}
public Position getNextFrom(Position start){
return new Position(start.getX()+change.getX(),start.getX()+change.getY());
}
#Override
public Collection<Move> getPossibleMoves(Position start, Board board){
Collection<Move> moves = new HashSet<>();
for(Direction direction : Direction.values()){
Position current = direction.getNextFrom(start);
while( board.isOnBorad(current.getX(),current.getY())
&& board.getActiveChessmanAt(current.getX(), current.getY()).isAlive()){
moves.add(p-> new Position(current.getX(),current.getY());
}
}
return moves;
}
}
Related
I'm having problems with my implementation of the MiniMax algoritm for my chess game. Most parts of it seems to work, but it either never makes the good moves or something is wrong with the evaluation (score based of both players active pieces) of them.
For example if I set up check (fool's mate for example) the ai does something random instead of killing the king. I really can't pin out what I'm doing wrong.
The class that evaluates the board, StandardBoardEvaluator, seems to work after some testing, so the problem is most likely somewhere within the MiniMax implementation. The game is made up from a class Board, which has and 2D array with 8x8 objects of my own class Square, which in itself has a reference to an Piece (that can be null, or any of the typical chess pieces).
In the algoritm i constantly makes new Board instances as going down the searchthree, which is why i made these "deep clone" constructors in Board and Square, so that does not seem to be the problem. Like this:
public Board(Board originalBoard) {
this.turnIsWhite = originalBoard.getTurnIsWhite();
winner = null;
squares = new Square[8][8];
for (int rank=0; rank<squares.length; rank++) {
for(int file=0; file<squares[rank].length; file++) {
squares[rank][file] = new Square(originalBoard.getSquare(posStringFromFileRank(rank, file)));
}
}
}
AND
public Square(Square originalSquare) {
this.pos = new String(originalSquare.getPos());
this.piece = originalSquare.getPiece();
}
I have an typical command class, MovePiece, for moving pieces. This uses another class, MoveCheck, to check if the move command is legal. MovePiece returns a boolean representing if the move is legal. Both these classes have been heavily tested and are working, so I don't think the problem is within these classes.
Here is the algoritm:
public class MiniMax implements MoveStrategy{
BoardEveluator bV;
MoveGenerator mGen;
int depth;
public MiniMax(int depth){
bV = new StandardBoardEvaluator();
mGen = new MoveGenerator();
this.depth = depth;
}
#Override
public MovePiece execute(Board board) {
MovePiece bestMove = null;
int lowestValue = Integer.MAX_VALUE;
int highestValue = Integer.MIN_VALUE;
int currentValue = 0;
String color = (board.getTurnIsWhite() ? "white" : "black");
System.out.println(color + " is evaluation best move with MiniMax depth " + depth);
List<MovePiece> allPossibleMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());
for (MovePiece mp : allPossibleMoves){
Board tempBoard = new Board(board);
mp.setBoard(tempBoard);
if (mp.execute()){
currentValue = tempBoard.getTurnIsWhite() ? min(tempBoard, depth -1) : max(tempBoard, depth -1);
if (board.getTurnIsWhite() && currentValue >= highestValue){
highestValue = currentValue;
bestMove = mp;
}
else if (!board.getTurnIsWhite() && currentValue <= lowestValue){
lowestValue = currentValue;
bestMove = mp;
}
mp.unexecute();
}
}
return bestMove;
}
int min (Board board, int depth){
if (depth == 0 || board.getWinner() != null){
return bV.eveluate(board);
}
int lowestValue = Integer.MAX_VALUE;
List<MovePiece> legalMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());
for (MovePiece mp : legalMoves){
Board tempBoard = new Board(board);
mp.setBoard(tempBoard);
if (mp.execute()){
int currentValue = max(tempBoard, depth - 1);
if (currentValue <= lowestValue){
lowestValue = currentValue;
}
mp.unexecute();
}
}
return lowestValue;
}
int max (Board board, int depth){
if (depth == 0 || board.getWinner() != null){
return bV.eveluate(board);
}
int highestValue = Integer.MIN_VALUE;
List<MovePiece> legalMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());
for (MovePiece mp : legalMoves){
Board tempBoard = new Board(board);
mp.setBoard(tempBoard);
if (mp.execute()){
int currentValue = min(tempBoard, depth - 1);
if (currentValue >= highestValue){
highestValue = currentValue;
}
mp.unexecute();
}
}
return highestValue;
}
And the evalutor class
public class StandardBoardEvaluator implements BoardEveluator {
private int scorePlayer(Board board, boolean isWhite){
return pieceValue(board, isWhite) + mobolity(isWhite, board);
}
private int mobolity(boolean isWhite, Board board){
return (int) (board.getActiveSquares(isWhite).size() * 1.5);
}
private static int pieceValue(Board board, boolean isWhite){
int piceValueScore = 0;
for (Square square : board.getActiveSquares(isWhite)){
piceValueScore += square.getPiece().getPieceValue();
}
return piceValueScore;
}
#Override
public int eveluate(Board board) {
return scorePlayer(board, true) - scorePlayer(board, false);
}
}
Here is the MovePiece class:
private Square from;
private Square to;
private Board board;
private MoveCheck mCheck;
private RulesCheck rCheck;
private boolean done = false;
private Piece killed;
public MovePiece(Board board, String from, String to) {
this.board = board;
this.from = board.getSquare(from);
this.to = board.getSquare(to);
mCheck = new MoveCheck();
}
public MovePiece(Board board, Square from, Square to) {
this.board = board;
this.from = from;
this.to = to;
mCheck = new MoveCheck();
rCheck = new RulesCheck(board);
}
public void setBoard(Board board) {
this.board = board;
}
public Board getBoard() {
return board;
}
public Square getFrom() {
return from;
}
public Square getTo() {
return to;
}
public void setFrom(Square from) {
this.from = from;
}
public void setTo(Square to) {
this.to = to;
}
public void setFrom(String from) {
this.from = board.getSquare(from);
}
public void setTo(String to) {
this.to = board.getSquare(to);
}
#Override
public boolean execute() {
rCheck = new RulesCheck(board);
if (done) {
board.movePiece(from, to);
return true;
}
else if (mCheck.isLegal(board, from, to)){
if (to.getPiece() != null) {
killed = to.getPiece();
rCheck.winCheck(killed);
}
board.setGameOutput("Moved " + from.pieceToString() + " at " + from.getPos() + " - to " + to.getPos() + "(" + to.pieceToString() + ")");
board.movePiece(from, to);
rCheck.checkPromotion(to);
done = true;
return true;
}
return false;
}
#Override
public void unexecute() {
if (to.getPiece().getClass() == Pawn.class)
((Pawn) to.getPiece()).decreaseMoves();
board.movePiece(to, from);
if (killed != null) {
to.setPiece(killed);
}
}
The MoveCheck class merely looks if the move is legal for the piece (path is clear, target is an enemy or empty and so on), don't think it's relevant for my problem since the code is tested and works.
The piece value is declared as an int in the subclasses (all the types of pieces) of the abstract class Piece. 100 points for a pawn, 300 for bishop and knight, 500 for rook, 900 for queen and 10 000 for the king.
If anyone could help me figure out the problem i would be eternally grateful! Please let me know if you need to se some other code i haven't showed.
You haven't shared the MovePiece implementation neither the main game loop, but I indentified two possible problems inside MiniMax.execute method:
currentValue = tempBoard.getTurnIsWhite() ? min(tempBoard, depth -1) : max(tempBoard, depth -1)
According to the above code, you are assuming that the MinMax player will always be black, as it evaluates min for white and max for black. For a generic algorithm this is a wrong assumption, don't know if it works for you though.
Second thing is after calling mp.execute() and assigning bestMove = mp you call mp.unexecute(), so effectively call bestMove.unexecute() since the variables point to the same object.
Please consider the suggestions above and if it does not fix the problem, share the abovementioned implementation pieces.
(I hope this is not a duplicate as the many questions I came into do not fit my need)
I'm developping a 2D grid based game with 2 players with grid. There are two players: blue and red, each one places a stone in cells. So I want to find a path passing throught all cells with the same color back to the starting point, BUT ONLY if there is at least ONE cell that contains opponent's stone.
From the screenshot above: The red stones here in the upper right do not form a valid path. And those in the center are not forming a path neither even though that should be one.
I'm able to find a path but it is somehow broken, it doesn't work as expected.
EDIT:
Pather class
public class Pather {
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT(0,1,-1),
RIGHT(1,1,0),
DOWN_RIGHT(2,1,1),
DOWN(3,0,1),
DOWN_LEFT(4,-1,1),
LEFT(5,-1,0),
UP_LEFT(6,-1,-1),
UP(7,0,-1);
public int index, x, y;
Neighbor(int index, int x, int y){
this.index = index;
this.x = x;
this.y = y;
}
}
private static Neighbor[] neighbors = Neighbor.values();
public static ArrayList<Path> findPaths(Stone[][] gameBoard){
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Point> checkedPoints = new ArrayList<>();
for (int i = 0; i < gameBoard.length ; i++) {
for (int j = 0; j < gameBoard[0].length; j++) {
if(gameBoard[i][j] != null){
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (i,j);
if(!checkedPoints.contains(origin)) {
potentialPath.add(origin);
checkedPoints.add(origin);
potentialPath = findPath(gameBoard, i, j, potentialPath, gameBoard[i][j].getPaint(), checkedPoints, Neighbor.RIGHT.index); //Changed from Neighbor.DOWN.index
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[i][j].getPaint()));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(Stone[][] gameBoard, int x, int y, ArrayList<Point> path, Paint color, ArrayList<Point> checkedPoints, int cameFrom){
int startClockwiseScanAtDirection = cameFrom + 5;
for (int i = startClockwiseScanAtDirection; i < startClockwiseScanAtDirection + 7; i++) {
// avoid ArrayIndexOutOfBounds
if(x+neighbors[i%8].x < 0 || y+neighbors[i%8].y < 0 || x+neighbors[i%8].x >= gameBoard.length || y+neighbors[i%8].y >= gameBoard[0].length)
continue;
// check if there's a stone that matches the current stone, we're scanning around
if(gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y] != null && gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y].getPaint() == color){
// found one
Point nextStone = new Point(x+neighbors[i%8].x,y+neighbors[i%8].y);
// is the point we just found the origin of the path?
if(nextStone.equals(path.get(0)) && path.size() > MIN_PATH_LENGTH) { //This seems to prevent drawing a path when we have less stone to form a path with
path.add(nextStone);
checkedPoints.add(nextStone);
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard,x+neighbors[i%8].x, y+neighbors[i%8].y, path, color, checkedPoints, i%8);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
Path class
public class Path {
public Paint getColor() {
return color;
}
public void setColor(Paint color) {
this.color = color;
}
public ArrayList<Point> getCoordinateList() {
return coordinateList;
}
public void setCoordinateList(ArrayList<Point> coordinateList) {
this.coordinateList = coordinateList;
}
private ArrayList<Point> coordinateList;
private Paint color;
public Path(ArrayList<Point> coordinatePath, Paint color){
this.coordinateList = coordinatePath;
this.color = color;
}
#Override
public String toString() {
return coordinateList.toString();
}
}
Here some case test:
Called in the MainActivity's onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gameGrid = findViewById(R.id.gameGrid);
bluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bluePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bluePaint.setColor(Color.BLUE);
redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setStyle(Paint.Style.FILL);
redPaint.setColor(Color.RED);
bgrBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrBluePaint.setStyle(Paint.Style.STROKE);
bgrBluePaint.setStrokeWidth(bgrStrokeWdth);
bgrBluePaint.setColor(Color.BLUE);
bgrRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrRedPaint.setStyle(Paint.Style.STROKE);
bgrRedPaint.setStrokeWidth(bgrStrokeWdth);
bgrRedPaint.setColor(Color.RED);
bluePlayer = new Stone(1,bluePaint, bgrBluePaint);
redPlayer = new Stone(2, redPaint, bgrRedPaint);
gameBoard = new Stone[100][100];
gameBoard[47][47]= redPlayer;
gameBoard[46][47]= bluePlayer;
gameBoard[44][48]= redPlayer; //REDs form a path when you place this stone in the last positioon
gameBoard[44][49]= redPlayer;
gameBoard[45][47]= redPlayer;
gameBoard[45][48]= bluePlayer;
gameBoard[45][49]= bluePlayer;
gameBoard[45][50]= redPlayer;
gameBoard[46][50]= bluePlayer;
gameBoard[46][49]= redPlayer;
gameBoard[46][48]= redPlayer;
gameBoard[47][50]= bluePlayer;
gameBoard[47][48]= bluePlayer;
gameBoard[47][49]= redPlayer;
gameBoard[48][50]= redPlayer;
gameBoard[48][49]= redPlayer;
gameBoard[48][48]= redPlayer;
gameBoard[49][50]= bluePlayer;
gameBoard[48][51]= redPlayer;
gameBoard[44][50] = bluePlayer;
ArrayList<Path> paths = Pather.findPaths(gameBoard);
gameGrid.setPaths(paths);
gameGrid.setGameBoard(gameBoard);
}
Placing stones at the following positions clears the path:
//Adding the following deletes the path
gameBoard[43][50] = redPlayer; //Adding this one in last position clears the path
gameBoard[45][51] = redPlayer;
I need to figure out how to make a condition that check for an opponent nearby first then validate the path.
EDIT 2:
Stone.java
public class Stone{
private int _player;
private Paint _paint, _bgrPaint;
public Stone(int player, Paint paint, Paint bgrPaint){
_player = player;
_paint = paint;
_bgrPaint = bgrPaint;
}
public int getPlayer() {
return _player;
}
public Paint getPaint() {
return _paint;
}
public Paint get_bgrPaint() {
return _bgrPaint;
}
}
Point.java
public class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object point) {
return this.x == ((Point) point).x && this.y == ((Point) point).y;
}
#Override
public String toString() {
return "("+x+","+y+")";
}
}
Screenshoot of what a valid path should look
A more-or-less standard way to approach this kind of problem is a "sweep line" algorithm. For simplicity, say we're looking for blue paths wrapping red points.
(You can process red paths wrapping blue points at the same time or in a second pass, but you can work that out later.)
You can search for "sweep line algorithm" to see how they work in related applications. The Wikipedia page isn't bad.
For this problem, the sweep line is a set of y-intervals. It's initialized using the leftmost (least x) blue point(s). It gets one interval for each vertically adjacent set of blue points. Each interval represents a vertical slice through a potential blue polygon.
The rest of the algorithm is to design the rules needed to update the scan line when it is moved one position to the right, incrementing x. This will be a matter of updating each interval. When a step finds a disconnected set of vertically adjacent points, a new interval is added. In some cases, intervals will "die out" because the potential polygon boundary dead-ends (think of a C shape). In other cases, they will "merge" because, at the corresponding x-coordinate, there is a set of 1 or more vertically adjacent connecting points. In still other cases, the polygon will complete successfully with a final set of 1 or more vertically adjacent points.
The details will be fiddly, but not hard to work out by case analysis.
To trace successful polygons, intervals can include two chains of preceding points: the upper and lower polygon boundaries.
The last consideration is whether a successfully closed polygon encloses at least one red point. But this is easy. If for any x-coordinate, the interval representing a polygon bracketed a red point, then the answer is yes. This can be recorded as an initially false boolean maintained in the interval, which is set true every time such a red point is seen. When a polygon is successfully closed, check the flag to see whether it should be used or not.
All the above can be made efficient for very large grids by using suitable data structures: interval trees for example. But if the grid is comparatively small, it should be fine to use simple lists. At any rate, consider prototyping it with a list for the sweep line first first and optimize with more complicated data structures later if needed.
As I wrote in my comments, without mvce it is very hard to offer detailed help.
From what I see in the code I figure you are trying to map all cyclic single-color paths on the board.
I made some documented changes in the code, hoping (without being able to properly check it) that it may help you improve your code.
Note that as Stone class was not posted, I changed the representation of the board to int[][]
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Phather {
private static final int RED = 2, BLUE = 1;
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT ( 1,-1),
RIGHT ( 1, 0),
DOWN_RIGHT( 1, 1),
DOWN ( 0, 1),
DOWN_LEFT (-1, 1),
LEFT (-1, 0),
UP_LEFT (-1,-1),
UP ( 0,-1);
int x, y;
Neighbor(int x, int y){
this.x = x;
this.y = y;
}
}
public static Set<Path> findPaths(int[][] gameBoard){
//use set to prevent duplicate paths
Set<Path> paths = new HashSet<>();
for (int x = 0; x < gameBoard.length ; x++) {
for (int y = 0; y < gameBoard[0].length; y++) {
//note that array indexes are [y][x] while point arguments are x,y
if(gameBoard[y][x] != 0){
//use set to prevent duplicate elements. initialize it to allow for
//overlapping paths (paths that contain some shared points)
Set<Point> checkedPoints = new HashSet<>();
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (x,y);
if(checkedPoints.add(origin)) { //add returns false if duplicate
potentialPath.add(origin);
potentialPath = findPath(gameBoard, x, y, potentialPath, checkedPoints);
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[y][x]));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(int[][] gameBoard, int x, int y,
ArrayList<Point> path, Set<Point> checkedPoints){
int color = gameBoard[y][x]; //no need for color as argument. get from stone
for(Neighbor neighbor : Neighbor.values()) {
int neighborX = x + neighbor.x, neighborY = y + neighbor.y;
// avoid ArrayIndexOutOfBounds
//todo: refactor to method isValidAddress(x,y,maxX, maxY)
if((neighborX < 0) || ( neighborY < 0) || (neighborY >= gameBoard.length)
|| (neighborX >= gameBoard[0].length)) {
continue;
}
// check if there's a stone that matches the current stone, we're scanning around
if((gameBoard[neighborY][neighborX] != 0) && (gameBoard[neighborY][neighborX] == color)){
// found one
Point nextStone = new Point(neighborX,neighborY);
// is the point we just found the origin of the path ?
if(nextStone.equals(path.get(0)) && (path.size() > MIN_PATH_LENGTH)) {
path.add(nextStone); //do you want it in path twice ?
//checkedPoints.add(nextStone); //if added to path before, it is already in checkedPoints
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard, neighborX, neighborY, path, checkedPoints);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
class Path {
private ArrayList<Point> coordinateList;
private int color;
Path(ArrayList<Point> coordinatePath, int color){
coordinateList = coordinatePath;
this.color = color;
}
int getColor() { return color; }
#Override
public String toString() {
return coordinateList.toString();
}
List<Point> getPoints() { return coordinateList; }
int size() { return coordinateList.size(); }
#Override
public boolean equals(Object p){
if (p == this) { return true; }
if (p == null) { return false;}
if (!(p instanceof Path)) {return false; }
Path path = (Path)p;
return getPoints().containsAll(path.getPoints())
&& path.getPoints().containsAll(getPoints());
}
}
I have a problem on designing a proper mapping.
i'm developing a top-down rpg game where you only see parts (chunks) of the map at runtime - when moving from one chunk to to another the new chunk data is loaded.
depending on my position different data is loaded.
currently looks my code like this (but certainly longer)
public static final int getChunkIdForPos(int x, int y){
switch (x) {
case 1248:
switch (y) {
case 1247: return R.raw.overworld_512;
case 1248: return R.raw.overworld_528;
case 1249: return R.raw.overworld_544;
default: break;}break;
case 1249:
switch (y) {
case 1247: return R.raw.overworld_513;
case 1248: return R.raw.overworld_529;
case 1249: return R.raw.overworld_545;
default: break;}break;
}
return R.raw.overworld_161;
}
problem is that this code is not easy to read and not easy to maintain. Any Ideas on how to do better?
i was thinking about using Map<Integer,<Map<Integer,Integer>> to map data but that requires to create the Map first. That also means you carry lots of 'dead' objects as keys around (same for SparseArray<SparseArray<>>)
Note: you can not determinate the returned values by any Math as suggested in this answer - Implemention a Lookup Table
the reason for choosing this design is to get an answer without creating any objects - the lookup just runs through two switch/case statements and provides an answer on the fly!
If you use a Point class with equals and hashcode implemented you can use them as keys in maps.
Now the actual lookup mechanism collapses to:
static final Map<Point, Integer> lookup = new HashMap<>();
static {
// Looks a lot like your case statement eh?
lookup.put(new Point(1248, 1247), 512);
lookup.put(new Point(1248, 1248), 528);
lookup.put(new Point(1248, 1249), 544);
lookup.put(new Point(1249, 1247), 513);
lookup.put(new Point(1249, 1248), 529);
lookup.put(new Point(1249, 1249), 545);
// Could also be populated by other statics.
}
public static final int getChunkIdForPos(int x, int y) {
Integer got = lookup.get(new Point(x, y));
return got != null ? got : 161;
}
The elegance of this is that the map is only ever built once. It is built at a predictable time (first reference to the class) and it can be added to by other modules (through a little tweaking).
Here's an example of a Point class that correctly implements equals and hashcode.
public static class Point {
final int x;
final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public String toString() {
return "{" + x + "," + y + '}';
}
#Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + this.x;
hash = 97 * hash + this.y;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Point other = (Point) obj;
if (this.x != other.x) {
return false;
}
if (this.y != other.y) {
return false;
}
return true;
}
}
ok, first things first: I limited the map from 10.000x10.000 to 256x256
still the code is not maintainable!
still the code is not extendable!
still best performance is using switch/case
the proper way to handle this is using a code generator. In a seperate project, thats not limited I create a worldmap-editor, that lets me see the all chunks in combination. Once the map is complete (hint: you can save/load maps now) the code emitter generates a class for you.
the magic is done from the codeEmitter class:
int[][] worldMap = new int[256][256]; //generated via GUI
File file = new File(fileName);
BufferedWriter br = new BufferedWriter(new FileWriter(file));
//writing header
for (int dy = 0; dy < 256; dy++) {
br.write(" public static int row_"+dy+"(int x){\n");
br.write(" switch (x) {\n");
for (int dx = 0; dx < 256; dx++) {
br.write(" case " + dx + ": return " + worldMap[dx][dy] + ";\n");
}
br.write(" default: return 0;\n");
br.write(" }\n");
br.write(" }\n");
br.write("\n");
}
//writing trailer
the result is very similar to the requested code from the question
//this part of genCode is not explained above
public static final int getIdForPos(int x, int y, int area){
switch (y) {
case 0: return row_0(x);
case 1: return row_1(x);
//.. lots of rows
default: return 0;
}
}
//but this part is generated with the code above
public static int row_0(int x){
switch (x) {
case 0: return 4711;
case 1: return 815;
//.. lots of columns
default: return 0;
}
}
i'm a beginner to inheritance in programming, i'm trying to desgin a RGB colour mixer here using the inheritance design, it have 3 classes, RedScrollBar, GreenScrollBar and BlueScrollBar. I tried to create a parent class first, called ScrollBar and tried to extend it to 3 classes. But then i realised for each class, i would need to change their variable names too, for example:
class BlueScrollBar {
//establish line x1 y1 and x2 y2
float blueLX1;
float blueLY1;
float blueLX2;
float blueLY2;
//establish the box, x, y, width and height;
float blueBX;
float blueBY;
float bW = 20;
float bH = 20;
boolean blueMouseOver = false;
boolean blueBoxLocked = false;
float blueYOffset = 0.0;
BlueScrollBar(int lx1, int ly1, int lx2, int ly2) {
blueLX1 = lx1;
blueLY1 = ly1;
blueLX2 = lx2;
blueLY2 = ly2;
blueBX = lx1;
blueBY = ly2/2;
}
void draw(){
if(mouseX >=blueBX-bW/2 && mouseX <=blueBX+bW/2 && mouseY >=blueBY-bH/2 && mouseY <=blueBY+bH/2 ){
fill(0);
blueMouseOver = true;
} else {
fill(255);
blueMouseOver = false;
}
line(blueLX1, blueLY1, blueLX2, blueLY2);
rect(blueBX, blueBY, bW, bH);
if (blueBY <= blueLY1 || blueBY >= blueLY2) {
blueBoxLocked = false;
}
}
void mousePressed(){
if(blueMouseOver){
blueBoxLocked = true;
blueBY = mouseY - blueYOffset;
} else {
blueBoxLocked = false;
}
}
void mouseDragged(){
if(blueBoxLocked){
blueBY = mouseY - blueYOffset;
}
}
void mouseReleased(){
blueBoxLocked = false;
}
}
and for the RedScrollBar or GreenScrollBar, i could literally copy paste the same code to create a new class but i need to change all the variable that contains the word 'blue' to 'red' or 'green' for it to work. What's a better way to do it? Any help would be appreciated.
You were on the right track to start! Make a class named Scrollbar and have the color be set in the constructor of the class (through an enum if you only want to select from a certain set of colors). That way you have one class to solve your problem.
I assume you know how to make a constructor, but if you don't comment on this answer and I will show you.
Edit 1:
Ok, so when you create a class in java a constructor (a fancy name for the function used to create an object of the class) is generated for you if you do not explicitly define one. Whenever you call MyClass test = new MyClass(); chances are you are just using the constructor that is auto generated (no arguments need to be passed to it.)
However, you are going to need your own custom constructor so you would do something like this.
public class ScrollBar{
Color color;
//your constructor.
public ScrollBar(Color c){
this.color = c;
}
}
That being said I'm no sure how you are implementing the color or what framework you are working in so take the code above with a grain of salt.
Note that if you create your own constructor, the default one will not be generated for you. So this would give you an error:
ScrollBar test = new ScrollBar(); // :( error
ScrollBar test = new ScrollBar(RED); // :) good
Edit 2:
Sorry to mislead you in that way. I was trying to keep my code above very general since I am not sure how you were dealing with color in your program. However you were trying to implement the red/blue/green will work with this method. If you truly only want the three colors you listed, you could just pass an integer to your constructor where 0,1,2 correspond to a specific color you want to use.
ie)
public class ScrollBar{
int c = 0; //default to red if the user gives a bad value
public ScrollBar(int c){
if(c >=0 && c <=2){ //check bounds
this.color = c;
}
}
public setColor(){
if(this.color == 0){
//do something with red
}
else if(this.color == 1){
//do something with blue
}
else{
//do something with green
}
}
}
I know this kind of question has been asked before, but i was unable to solve my doubts.
I have a simple Othello Engine (it plays very well actually), that uses the class below to get the best move:
import java.util.*;
import java.util.concurrent.*;
public class MinimaxOthello implements Runnable
{
private CountDownLatch doneSignal;
private int maxDepth;
private int calls;
private OthelloMove bestFound;
private OthelloBoard board;
private static float INFINITY = Float.MAX_VALUE/1000;
private boolean solve = false;
private Comparator<OthelloMove> comparator = Collections.reverseOrder(new MoveComparator());
public MinimaxOthello (OthelloBoard board, int maxDepth, CountDownLatch doneSignal, boolean solve)
{
this.board = board;
this.bestFound = new OthelloMove();
bestFound.setPlayer(board.getCurrentPlayer());
this.maxDepth = maxDepth;
this.doneSignal = doneSignal;
this.solve = solve;
}
public OthelloMove getBestFound()
{
return this.bestFound;
}
public void run()
{
float val = minimax(board, bestFound, -INFINITY, INFINITY, 0);
System.out.println("calls: " + calls);
System.out.println("eval: " + val);
System.out.println();
doneSignal.countDown();
}
private float minimax(OthelloBoard board, OthelloMove best, float alpha, float beta, int depth)
{
calls++;
OthelloMove garbage = new OthelloMove();
int currentPlayer = board.getCurrentPlayer();
if (board.checkEnd())
{
int bd = board.countDiscs(OthelloBoard.BLACK);
int wd = board.countDiscs(OthelloBoard.WHITE);
if ((bd > wd) && currentPlayer == OthelloBoard.BLACK)
{
return INFINITY/10;
}
else if ((bd < wd) && currentPlayer == OthelloBoard.BLACK)
{
return -INFINITY/10;
}
else if ((bd > wd) && currentPlayer == OthelloBoard.WHITE)
{
return -INFINITY/10;
}
else if ((bd < wd) && currentPlayer == OthelloBoard.WHITE)
{
return INFINITY/10;
}
else
{
return 0.0f;
}
}
if (!solve)
{
if (depth == maxDepth)
return OthelloHeuristics.eval(currentPlayer, board);
}
ArrayList<OthelloMove> moves = board.getAllMoves(currentPlayer);
if (moves.size() > 1)
{
OthelloHeuristics.scoreMoves(moves);
Collections.sort(moves, comparator);
}
for (OthelloMove mv : moves)
{
board.makeMove(mv);
float score = - minimax(board, garbage, -beta, -alpha, depth + 1);
board.undoMove(mv);
if(score > alpha)
{
alpha = score;
best.setFlipSquares(mv.getFlipSquares());
best.setIdx(mv.getIdx());
best.setPlayer(mv.getPlayer());
}
if (alpha >= beta)
break;
}
return alpha;
}
}
I have a bestFound instance variable and my doubt is, why a have to call
OthelloMove garbage = new OthelloMove();
and pass it along? The code works, but it seems very weird to me!
Is there a 'better' way to get the best move or the Principal Variation?
I really not a recursion expert, and this is very very hard to debug and visualize.
Thanks!
**PS: You can clone it at https://github.com/fernandotenorio/
It looks like you can get rid of the best parameter to minimax, thereby eliminating the need for garbage, and then replace best with this.bestFound. Only set bestFound's attributes if depth = 0.
You can get the principal variation by making this.bestFound an initially empty list. Before the moves loop, create a new move. In the if (score > alpha) part, set its attributes the same as you do now. Push the move to the list right after the loop. The principal variation will then be the reverse of the list.
If it's important, here are some changes you can make to improve the multi-threadability of your class:
Instead of storing the bestFound list as an instance variable, make it a local variable in run and add it as a parameter to minimax
Make Board.makeMove not modify the board, but instead return a new instance of the board with the move applied. You can implement that by cloning the board and applying your move code to the clone instead of mutating this. Then, pass the cloned board to the next invocation of minimax.
The second argument of minimax is used to return the best move.
The business with garbage is used to keep the best move for each turn separate. With the code you've provided, this is not important. But if you wanted to produce a sequence of moves from the current board to the end of the game, you would need to have them be separate move objects.
Using a separate best-move object for each turn allows you to do a number of tricks with threading. First, you might want to limit the thinking time of the Othello AI. Tracking the best move separately at each level means that you always have the best move so far available. It also means that you could cache the best move for a board and look that up in future minimax searches.
Second, you might want to search for the best move in parallel, and this is trivial to implement when each minimax call is independent.