Check winner from arraylist of players in card game - java

My card game needs a method to check the winner. Without the draw if statement, it works to check that a winning value. However I feel that without a draw check the rest of the project is redundant. How can I do this properly?
public String winChecker(){
Player winningPlayer = players.get(0);
for (Player player : players) {
if (player.getOverallHandValue() > winningPlayer.getOverallHandValue()){
winningPlayer = player;
}
if (player.getOverallHandValue() == winningPlayer.getOverallHandValue()){
return "draw!";
}
return winningPlayer.getName();
}
}
//...

public String winChecker(){
boolean many = false;
boolean initial = true;
Player winningPlayer = players.get(0);
for (Player player : players) {
if (player.getOverallHandValue() > winningPlayer.getOverallHandValue()){
winningPlayer = player;
many = false;
}
if (player.getOverallHandValue() == winningPlayer.getOverallHandValue()){
if (initial){
initial = false;
} else {
many = true;
}
}
}
if (many) {
return "draw";
}
return winningPlayer.getName();
}
is what you're looking for. In the current code there was a bug which was that in case the results were:
1 1 2 3 4 5
current code returned "draw" which is obviously wrong. Look, when you're trying to decide which of the players won, you can't decide who is the winner before reading scores of all players.
Let's describe both algorithms in English.
Set 0th element of players list as current winner.
loop through all players
if current player has a better score then currently winning, set the current player to currently winning.
if they have the same score, decide that the game is drawn. Look, that on the first iteration it's always true!
and the second algorithm
Set 0th element of players list as current winner.
loop through all players
if current player has a better score then currently winning, set the current player to currently winning and mark that there is a single leader at the moment.
if they have the same score, mark that there are at least two players on lead so there is possibility of a draw.

As this code stands you will always get "draw" returned.
If the players collection is like this
["Player A", "Player B", "Player C"]
Then this line of code
Player winningPlayer = players.get(0);
will get you Player A.
The problem is, when you iterate over players, the first iteration will get you Player A as well.
Therefore
if (player.getOverallHandValue() == winningPlayer.getOverallHandValue())
condition will always be true in the first iteration itself.
I think figuring out a way to fix this is best left to your own devices.

Related

How do I design a algorithm to predict the outcome of a game with both players playing optimally?

Currently, I am looking for a recursive solution to predict the outcome of a game with both players playing optimally. The game is played between two players.
I am given a sequence of numbers in the form of an array and always want to draw a number from one of the edge sides (i.e. start or end of the array). Depending on which player's turn it is, the points are credited and it is the next player's turn. This player now also chooses a number, which is at the edge of the number array. At the end the player with the larger sum has won.
In the graphic all possible game courses for four starting numbers are visualized in a binary tree. I am now wondering how to design a recursive algorithm that takes all these possible combinations into account and finally returns the score of the winning player if both players play optimally.
When you talk about optimal game results I assume that the player always chooses the highest number.
You will need to add deque pakages
import java.util.ArrayDeque;
import java.util.Deque;
Here you have a method that gives an array with the player A and player B results.
private static int[] optimalGameResults(int[] board) {
boolean turnA=true;
int sumA=0,sumB=0;
Deque<Integer> deque = new ArrayDeque<>();
for (int n: board) {
deque.offer(n);
}
while (deque.size()>0){
if(turnA){
sumA = sumOptimal(sumA, deque);
turnA=false;
}else{
sumB = sumOptimal(sumB, deque);
turnA=true;
}
}
int[] abResults={sumA,sumB};
return abResults;
}
It also needs an additional method to know which value remove (optimization)
private static int sumOptimal(int sum, Deque<Integer> deque) {
if(deque.getFirst()> deque.getLast()){
sum += deque.removeFirst();
}else {
sum += deque.removeLast();
}
return sum;
}

Minmax algorithm doesn't return direct child of root (returns illegal move)

I am trying to create "AI" for Nine Men's Morris but I got hardstuck on minMax algorithm. Summing up, I was trying to find the issue for over 10h but didn't manage to. (debugging this recursion is nasty or I am doing it badly or both)
Since I started doubting everything I wrote I decided to post my issue so someone can find anything wrong in my version of minMax. I realise it is really hard task without the whole application so any suggestions where I should triple check my code are also very welcome.
Here is link to the video, explaining minMax, on which I based my implementation: https://www.youtube.com/watch?v=l-hh51ncgDI (First video that pops up on yt after searching for minmax - just in case you want to watch the video and don't want to click the link)
My minMax without alpha-beta pruning:
//turn - tells which player is going to move
//gameStage - what action can be done in this move, where possible actions are: put pawn, move pawn, take opponent's pawn
//depth - tells how far down the game tree should minMax go
//spots - game board
private int minMax(int depth, Turn turn, GameStage gameStage, Spot[] spots){
if(depth==0){
return evaluateBoard(spots);
}
//in my scenario I am playing as WHITE and "AI" is playing as BLACK
//since heuristic (evaluateBoard) returns number equal to black pawns - white pawns
//I have decided that in my minMax algorithm every white turn will try to minimize and black turn will try to maximize
//I dont know if this is correct approach but It seems logical to me so let me know if this is wrong
boolean isMaximizing = turn.equals(Turn.BLACK);
//get all possible (legal) actions based on circumstances
ArrayList<Action> children = gameManager.getAllPossibleActions(spots,turn,gameStage);
//this object will hold information about game circumstances after applying child move
//and this information will be passed in recursive call
ActionResult result;
//placeholder for value returned by minMax()
int eval;
//scenario for maximizing player
if(isMaximizing){
int maxEval = NEGATIVE_INF;
for (Action child : children){
//aplying possible action (child) and passing its result to recursive call
result = gameManager.applyMove(child,turn,spots);
//evaluate child move
eval = minMax(depth-1,result.getTurn(),result.getGameStage(),result.getSpots());
//resets board (which is array of Spots) so that board is not changed after minMax algorithm
//because I am working on the original board to avoid time consuming copies
gameManager.unapplyMove(child,turn,spots,result);
if(maxEval<eval){
maxEval = eval;
//assign child with the biggest value to global static reference
Instances.theBestAction = child;
}
}
return maxEval;
}
//scenario for minimizing player - the same logic as for maximizing player but for minimizing
else{
int minEval = POSITIVE_INF;
for (Action child : children){
result = engine.getGameManager().applyMove(child,turn,spots);
eval = minMax(depth-1,result.getTurn(),result.getGameStage(),result.getSpots());
engine.getGameManager().unapplyMove(child,turn,spots,result);
if(minEval>eval){
minEval=eval;
Instances.theBestAction = child;
}
}
return minEval;
}
}
Simple heuristic for evaluation:
//calculates the difference between black pawns on board
//and white pawns on board
public int evaluateBoard(Spot[] spots) {
int value = 0;
for (Spot spot : spots) {
if (spot.getTurn().equals(Turn.BLACK)) {
value++;
}else if(spot.getTurn().equals(Turn.WHITE)){
value--;
}
}
return value;
}
My issue:
//the same parameters as in minMax() function
public void checkMove(GameStage gameStage, Turn turn, Spot[] spots) {
//one of these must be returned by minMax() function
//because these are the only legal actions that can be done in this turn
ArrayList<Action> possibleActions = gameManager.getAllPossibleActions(spots,turn,gameStage);
//I ignore int returned by minMax() because,
//after execution of this function, action choosed by minMax() is assigned
//to global static reference
minMax(1,turn,gameStage,spots);
//getting action choosed by minMax() from global
//static reference
Action aiAction = Instances.theBestAction;
//flag to check if aiAction is in possibleActions
boolean wasFound = false;
//find the same action returned by minMax() in possibleActions
//change the flag upon finding one
for(Action possibleAction : possibleActions){
if(possibleAction.getStartSpotId() == aiAction.getStartSpotId() &&
possibleAction.getEndSpotId() == aiAction.getEndSpotId() &&
possibleAction.getActionType().equals(aiAction.getActionType())){
wasFound = true;
break;
}
}
//when depth is equal to 1 it always is true
//because there is no other choice, but
//when depth>1 it really soon is false
//so direct child of root is not chosen
System.out.println("wasFound?: "+wasFound);
}
Is the idea behind my implementation of minMax algorithm correct?
I think the error might exist in that you are updating Instances.theBestAction even while evaluating child moves.
For example, lets say 'Move 4' is the true best move that will eventually be returned, but while evaluating 'Move 5', theBestAction is set to the best child action of 'Move 5'. From this point on, you won't update the original theBestAction back to 'Move 4'.
Perhaps just a simple condition that only sets theBestAction when depth == originalDepth?
Rather than using a global, you could also consider returning a struct/object that contains both the best score AND the move that earned the score.

Dealing with a specific rule of a card game

I am working on a card game and I'm facing a problem regarding the gameplay. So basically the rules of the game are that who finishes the hand first gets placed first, second second and so on. I am keeping a list of the players in a List<Player, and if one finishes I just skip the turn from him (checks if >= 1 cards in hand), until 1 player is left. Now, the rule is that if one finishes with an unbeatable card/move or everyone else passes for example, the turn should be to the next player and he is free to make a move. I've been struggling and haven't found a proper solution yet. How to achieve this?
I am currently saving the waste (last played list of cards) cards into a List<Card>, and I am keeping a Player instance lastPlayed for the last playing player, and a Player instance hasTurn for the current player on turn.
The turns are shifted in this method inside the Game class:
public void determineNextPlayerTurn()
{
if(playersLeft() > 1)
{
int i = players.indexOf(hasTurn) + 1;
if(i == 4)
i = 0;
hasTurn = players.get(i);
if(hasTurn.isPlaying())
{
if(!isHumanTurn())
{
display.disableButtons();
AI temp = (AI)players.get(i);
temp.onPlayerTurn(this);
}
else
display.enableButtons();
}
else
{
determineNextPlayerTurn();
}
}
else
newGame(0);
The method of AI that is called when it is an ai's turn:
public void onPlayerTurn(Game game)
{
selectCardsToPlay(game.getWaste(), game.getLastPlayedPlayer());
if(getSelectedCards().isEmpty())
{
game.determineNextPlayerTurn();
}
else
{
if(Moves.canBePlayed(getSelectedCards(), game.getWaste(), this, game.getLastPlayedPlayer()))
{
playMove(game);
game.determineNextPlayerTurn();
}
else
game.determineNextPlayerTurn();
}
}
While for the user, nothing is called as the determineNextPlayerTurn() is called within the ActionListener of the playing buttons (Play, Pass).
Note: Currently like this, if the scenario occurs with the human last playing there will be a stackoverflow for the reason that every ai is doing a Pass. Every other code is fine for now, but I need help with implementing a simple solution to that specific case, and if I am doing something wrong I am open.
Why don't you have a counter for the number of "passes" that are executed and then compare that to the number of players. If the passes are equal to the number of players - 1 then that would mean that following player should be free to make a move. Also what is an "unbeatable move" if there is such a thing then wouldn't you be able to classify the turn as that and then attach a boolean that checks the player for whether or not they have made one of these types of moves?

How to choose a spesific object from ArrayList

We are making a simple rpg game. Right now we have a class called battleground, where the player can fight monsters. We use a random generator to pick out a random monster when we start the fight. In the terminal/main method, we have commands like "attack" and "run", that will either do damage to the random monster, or make the player leave/quit the game. Right now, we are trying to add a command called "attack scariest", which will let the player fight against the hardest monster with the most damage(there are three to choose from in our main). We need a method to choose a specific object from the ArrayList monsters, based on damage. Does anyone have tips on how we do that?
This is our code in the Battleground class that starts the game:
public void startBattle() {
printWelcomeMessage();
boolean finished = false;
this.currentMonster = getRandomMonster();
if(this.currentMonster == null) {
return;
}
System.out.println("A random monster is chosen for you. Prepare to meet the mighty " + this.currentMonster.getName());
System.out.println("\n-------- Player Stats ---------");
System.out.println(this.player);
while(!finished && monsters.size() > 0) {
ArrayList<String> commands = reader.getInput();
if(isValidCommand(commands)) {
if(commands.contains("quit") && !this.currentMonster.isDead()){
System.out.println("You can't quit the game in the middle of a fight!");
}else if(commands.contains("quit") && this.currentMonster.isDead()) {
finished = true;
System.out.println();
printFinalStats();
System.out.println();
System.out.println("You have left the arena, and the game has ended.");
System.out.println();
}
if(commands.contains("run")) {
finished = true;
System.out.println("You are a coward and you lose 50 gold pieces...\n");
this.player.changeGold(-50);
printFinalStats();
System.out.println("\nThanks for playing...");
}else if(commands.contains("drink") && !this.currentMonster.isDead()){
Potion potion = (Potion) player.findItem(commands.get(1));
player.drinkPotion(potion);
}else if(commands.contains("equip") && !this.currentMonster.isDead()){
Weapons weapon = (Weapons) player.findItem(commands.get(1));
player.useWeapon(weapon);
} else if(commands.contains("attack") && !this.currentMonster.isDead()) {
if(this.player.attack(this.currentMonster)) {
playerWon();
if(this.monsters.size() > 0) {
System.out.println("\nThere are " + this.monsters.size() + " more monsters to beat\nType \"attack\" if you want to attack another monster, or \"quit\" if you want to end the game.");
} else {
System.out.println("\n\n#### Congratulations ####\nYou have beaten every single monster in the game. You are a true champion!");
printFinalStats();
finished = true;
}
} else if(this.currentMonster.attack(this.player)) {
printLosingMessage();
finished = true;
}
} else if(commands.contains("attack") && this.currentMonster.isDead() && this.monsters.size() > 0) {
this.currentMonster = getRandomMonster();
printContinueMessage(this.player, this.currentMonster);
this.player.changeHealth(50);
}
} else {
System.out.println("Please write a valid command. Valid commands are:");
printCommands();
}
}
}
This is the ArrayList of monsters in the main class:
Monster beelzebub = new Monster("Beelzebub", 60);
Monster witch = new Monster("Witch", 40);
Monster ogre = new Monster("Ogre", 80);
ArrayList<Monster> monsters = new ArrayList<>();
monsters.add(beelzebub);
monsters.add(witch);
monsters.add(ogre);
Battleground battleground = new Battleground(monsters, player, reader);
battleground.startBattle();
We appreciate any help!
Replace the ArrayList with a SortedSet or a PriorityQueue. Implement a Comparator for Monster class. Then just pick the first element of the monsters collection.
I can't see your Monster class but I'm assuming the second variable to the constructor is the 'damage level'? If not, ideally this should be something that belongs to each monster so you should be setting it somewhere.
So you have the array Monsters which has all the monsters with various levels of damage already. There are a lot of ways you could do this, but a simple way (given you only have 3 monsters) is to just iterate over your monsters and keep track of the monster with the highest damage, and then return that monster.
For example:
Monster findHardestMonster(ArrayList<Monster> monsters)
{
//Set to the first monster in the list ASSUMING you have
//at least one monster
Monster highestDamageMonster = monsters.get(0);
//Go through every monster in your array
for (Monster monster : monsters)
{
//record the highest so far
if(monster.getDamage() > highestDamageMonster.getDamage())
highestDamageMonster = monster;
}
}
//return it
return highestDamageMonster;
}
If you plan on removing monsters frequently (say, when they're defeated), it might be worth as another poster suggested to use a priority queue or some sort of ordered collection whereby the ordering is based on damage level instead. Then you won't have to iterate over the monsters every time to find the one with max damage.
for(int i=0;i<monsters.size()-1;i++){
Monster m=monsters.get(i);
Monster m2=monsters.get(i+1);
if(m.getDamage()<m2.getDamage()){
Monster rightMonster=m2;
}else{
Monster rightMonster=m;
}
}
This should work if i am not mistaken. i havent tried the code but maybe it gives you hints
The stream operations you can perform on the list will let you do this easily, by using max with a Comparator that ranks the monsters according to damage.
Comparator<Monster> orderByDamage =
(m1, m2) -> m1.getDamage() < m2.getDamage() ? -1
: m1.getDamage() == m2.getDamage() ? 0 : 1;
What this does is compare the damage of two monsters (m1 and m2). If the damage of m1 is less than that of m2, a negative number is returned. If they are equal, zero is returned and if m1 has more damage than m2, a positive number is returned.
Note: if getDamage() returns an Integer and not an int, replace m1.getDamage() == m2.getDamage() with Objects.equals(m1.getDamage(), m2.getDamage()).
Use this with your list to get the monster with the most damage:
Optional<Monster> scariest = monsters.stream().max(orderByDamage);
Note the return type, which is Optional<Monster>. If the list is not empty, the Optional will contain the scariest monster; if the list is empty, the optional will be empty - this will help avoid a NullPointerException if all monsters have been removed.
scariest.ifPresent(monster -> /* do something to the monster */);
You can also use this approach to get the monster with the least damage:
Optional<Monster> leastScary = monsters.stream().min(orderByDamage);

Sample code not doing as expected

Here is the algorithm (not working) Please let me know where the error is
Thanks
private void checkSouth(Location point, int player) {
//Loop through everything south
boolean isthereAnOppositePlayer=false;
int oppositePlayer=0;
//Set opposite player
if (player==1) {
oppositePlayer=2;
}else{
oppositePlayer=1;
}
for (int i = point.getVertical(); i < 8; i++) {
//Create a location point with the current location being compared
MyLocation locationBeingChecked= new MyLocation();
locationBeingChecked.setHorizontal(point.getHorizontal());
locationBeingChecked.setVertical(i);
int value = board[locationBeingChecked.getVertical()][locationBeingChecked.getHorizontal()];
//If the first checked is the opposite player
if (value==oppositePlayer) {
//Then potential to evaluate more
isthereAnOppositePlayer=true;
}
//If it isn't an opposite player, then break
if(!isthereAnOppositePlayer && value!=0){
break;
}
//If another of the player's piece found or 0, then end
if (isthereAnOppositePlayer && value==player || isthereAnOppositePlayer && value==0) {
break;
//end
}
//Add to number of players to flip
if(isthereAnOppositePlayer && value==oppositePlayer && value!=0){
//add to array
addToPiecesToTurn(locationBeingChecked);
}
}
}
It looks like the locations that got rotated back to the other player are the exact same as those rotated during the first move. I would guess that the array being populated by addToPiecesToTurn is perhaps not being cleared out between each move, so all the previous locations are still in there.
If you are storing the pieces to be turned in an ArrayList, you can use the clear() method to erase the contents of the collection between each turn.
Another possible problem is that you are checking for the opposite player, and then instantly beginning to populate addToPiecesToTurn. However, the pieces in that direction are not necessarily valid to be rotated unless they are "sandwiched" in by a second location containing the current player's piece. I don't think your code is properly checking for that case; when that happens, you'll want to somehow skip flipping those pieces to the other player, such as clearing out the array of piecesToTurn.
Edit: Looking at your current solution where you are implementing every direction separately, you are going to have a lot of duplicated code. If you think about what it means to walk along a certain direction, you can think of it as adjusting the x/y value by a "step" amount. The step amount could be -1 for backwards, 0 for no move, or 1 for forwards. Then you could create a single method that handles all directions without duplicating the logic:
private void checkDirection(Location point, int player, int yStep, int xStep) {
int x = point.getHorizontal() + xStep;
int y = point.getVertical() + yStep;
MyLocation locationBeingChecked = new MyLocation();
locationBeingChecked.setHorizontal(x);
locationBeingChecked.setVertical(y);
while (isValid(locationBeingChecked)) {
// do the logic here
x += xStep;
y += yStep;
locationBeingChecked = new MyLocation();
locationBeingChecked.setHorizontal(x);
locationBeingChecked.setVertical(y);
}
}
You would need to implement isValid to check that the location is valid, i.e., in the board. Then you could call this method for each direction:
// north
checkDirection(curPoint, curPlayer, -1, 0);
// north-east
checkDirection(curPoint, curPlayer, -1, 1);
// east
checkDirection(curPoint, curPlayer, 0, 1);
// etc
This is the sort of problem that is ripe for some unit testing. You could very easily set up a board, play a move, and validate the answer, and the test results would give plenty of insight into where your expectations and reality diverge.
why didn't you use a 2d array ?
each cell would contain an enum : EMPTY, PLAYER_1, PLAYER_2 .
then, in order to go over the cells, you simply use loops for each direction.
for example, upon clicking on a cell , checking to the right would be:
for(int x=pressedLocation.x+1;x<cells[pressedLocation.y].length;++x)
{
Cell cell=cells[pressedLocation.y][x];
if(cell==EMPTY||cell==currentPlayerCell)
break;
cells[pressedLocation.y][x]=currentPlayerCell;
}
checking from top to bottom would be:
for(int y=pressedLocation.y+1;y<cells.length;++y)
{
Cell cell=cells[y][pressedLocation.x];
if(cell==EMPTY||cell==currentPlayerCell)
break;
cells[y][pressedLocation.x]=currentPlayerCell;
}

Categories