Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

L8 #436

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

L8 #436

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions ComputerPlayer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package player
import gameUtensils.KalahaBoard
import scala.util.Random

sealed trait DecisionTree
case object Empty extends DecisionTree
case class Node(board: KalahaBoard, diffrence: Int, firstMove: Int, move0: DecisionTree, move1: DecisionTree, move2: DecisionTree, move3: DecisionTree, move4: DecisionTree, move5: DecisionTree) extends DecisionTree

class ComputerPlayer(override val playerID: Int, val currentBoardState: KalahaBoard) extends Player(playerID)
{
private val generateRandom = Random
private val maxDepth = 3

// @Override
// override def makeMove(): Int =
// Thread.sleep(50)
// var index = if (playerID == 1) then 0 else 7
// if (playerID == 1) then
// index = generateRandom.between(0, 6)
// while(currentBoardState.getNumberOfStonesInPit(index) == 0) {
// index = generateRandom.between(0, 6)
// }
// index
// else
// index = generateRandom.between(7, 13)
// while(currentBoardState.getNumberOfStonesInPit(index) == 0) {
// index = generateRandom.between(7, 13)
// }
// index

@Override
override def makeMove(): Int =
Thread.sleep(500)
var move = 0
if currentBoardState.onlyOneOption() != -1 then move = currentBoardState.onlyOneOption()
else
val tree = generateDecisionTree(currentBoardState, maxDepth)
move = treeMax(tree)._2
println("PLayer " +playerID +" made move: " + move)
move

def getBestMoveEnemy(boardState: KalahaBoard): (Int, Int) =
val enemyID = if playerID == 1 then 2 else 1
val offset = if playerID == 1 then 7 else 0
var (bestResult, bestMove): (Int, Int) = (-48, 0)

for(i<- 0 to 5)
var copyOfBoard: KalahaBoard = boardState.copy()
if copyOfBoard.isMoveCorrect(i + offset) == false then ()
else
copyOfBoard.performMoveOfOnePlayer(i)
val enemyResult = copyOfBoard.resultForPlayer(enemyID)
if enemyResult > bestResult then {bestResult = enemyResult; bestMove = i + offset} else ()

(bestResult, bestMove)

def generateDecisionTree(boardStart: KalahaBoard, depthStart: Int): DecisionTree =
def decisionTreeGenerator(boardState: KalahaBoard, depth: Int, move: Int, firstMove: Int): DecisionTree =
//println("TWORZNEIE DRZEWA")
depth match
case 0 => Empty
case _ =>
val nextBoard = boardState.copy()
if nextBoard.isMoveCorrect(move) == false then Empty else
val willThisMoveFinishGame = nextBoard.performMoveOfOnePlayer(move)
if willThisMoveFinishGame then Node(nextBoard, nextBoard.resultForPlayer(playerID), firstMove, Empty, Empty, Empty, Empty, Empty, Empty)
else
nextBoard.performMoveOfOnePlayer(getBestMoveEnemy(nextBoard)._2)
if nextBoard.getActivePlayer() == 1 then Node(nextBoard, nextBoard.resultForPlayer(playerID), firstMove, decisionTreeGenerator(nextBoard, depth - 1, 0, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 1, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 2, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 3, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 4, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 5, firstMove))
else Node(nextBoard, nextBoard.resultForPlayer(playerID), firstMove, decisionTreeGenerator(nextBoard, depth - 1, 7, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 8, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 9, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 10, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 11, firstMove), decisionTreeGenerator(nextBoard, depth - 1, 12, firstMove))

if playerID == 1 then Node(boardStart.copy(), -48, 1, decisionTreeGenerator(boardStart, depthStart, 0, 0), decisionTreeGenerator(boardStart, depthStart, 1, 1), decisionTreeGenerator(boardStart, depthStart, 2, 2), decisionTreeGenerator(boardStart, depthStart, 3, 3), decisionTreeGenerator(boardStart, depthStart, 4, 4), decisionTreeGenerator(boardStart, depthStart, 5, 5))
else Node(boardStart.copy(), -48, 7, decisionTreeGenerator(boardStart, depthStart, 7, 7), decisionTreeGenerator(boardStart, depthStart, 8, 8), decisionTreeGenerator(boardStart, depthStart, 9, 9), decisionTreeGenerator(boardStart, depthStart, 10, 10), decisionTreeGenerator(boardStart, depthStart, 11, 11), decisionTreeGenerator(boardStart, depthStart, 12, 12))

def treeMax(tree: DecisionTree): (Int, Int) =
def treeMaxtHelper(nodesToVisit: DecisionTree): (Int, Int) =
nodesToVisit match
case Empty => (-49, -49)
case Node(board, diffrence, firstMove, Empty, Empty, Empty, Empty, Empty, Empty) => (diffrence, firstMove)
case Node(board, diffrence, firstMove, m0, m1, m2, m3, m4, m5) =>
val (max0, max1, max2, max3, max4, max5) = (treeMaxtHelper(m0), treeMaxtHelper(m1), treeMaxtHelper(m2), treeMaxtHelper(m3), treeMaxtHelper(m4), treeMaxtHelper(m5))
chooseBetterMove((diffrence, firstMove), bestDiffrence(max0, max1, max2, max3, max4, max5))
treeMaxtHelper(tree)

def bestDiffrence(max0: (Int, Int), max1: (Int, Int), max2: (Int, Int), max3: (Int, Int), max4: (Int, Int), max5: (Int, Int)): (Int, Int) =
chooseBetterMove(max5, chooseBetterMove(chooseBetterMove(max0, max1), chooseBetterMove(max2, max3)))

def chooseBetterMove(max0: (Int, Int), max1: (Int, Int)): (Int, Int) =
if max0._1 > max1._1 then max0
else max1
}

28 changes: 28 additions & 0 deletions HumanPlayer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package player
import gameUtensils.KalahaBoard
import player.Player
import java.awt.event.ActionEvent
import scala.annotation.tailrec
import scala.io.StdIn.readInt

class HumanPlayer(override val playerID: Int, val currentBoardState: KalahaBoard) extends Player(playerID)
{
@Override
override def makeMove(): Int =
println("PLayer " +playerID +" please make your move!")
try {
val input = readInt()
if currentBoardState.isMoveCorrect(input) then
println("PLayer " +playerID +" made move: " + input)
input
else
println("You can't move stones from this pit!")
makeMove()
}
catch {
case e: Exception => println("Index of pit must be an Integer!")
makeMove()
}


}
144 changes: 144 additions & 0 deletions KalahaBoard.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package gameUtensils
import scala.annotation.tailrec
import scala.util.Random

class KalahaBoard(private var activePlayer: Int)
{
private val generateRandom = Random

private val INITIAL_NUMBER_OF_STONES = 4
// number of pits excluding base
private val NUMBER_OF_PITS_FOR_PLAYER = 6
private val NUMBER_OF_PITS = 2 * NUMBER_OF_PITS_FOR_PLAYER + 2
// characteristic indexes of players' pits
private val PLAYER_1_ID = 1
private val PLAYER_2_ID = 2
private val PLAYER_1_START_INDEX = 0
private val PLAYER_1_BASE_INDEX = NUMBER_OF_PITS_FOR_PLAYER
private val PLAYER_2_START_INDEX = PLAYER_1_BASE_INDEX + 1
private val PLAYER_2_BASE_INDEX = PLAYER_2_START_INDEX + NUMBER_OF_PITS_FOR_PLAYER
// prepare pits to game
private var pits: Array[Int] = Array.fill(NUMBER_OF_PITS)(INITIAL_NUMBER_OF_STONES)
pits(PLAYER_1_BASE_INDEX) = 0
pits(PLAYER_2_BASE_INDEX) = 0

def resultForPlayer(playerID: Int): Int =
if playerID == 1 then pits(PLAYER_1_BASE_INDEX) - pits(PLAYER_2_BASE_INDEX)
else pits(PLAYER_2_BASE_INDEX) - pits(PLAYER_1_BASE_INDEX)

def getNumberOfStonesInPit(pitIndex: Int): Int =
pits(pitIndex)

def getActivePlayer(): Int =
activePlayer

def copy(): KalahaBoard =
val board = new KalahaBoard(activePlayer)
board.pits = pits.clone()
board

def indexLegend(): String =
s"\n ( 12 ) ( 11 ) ( 10 ) ( 9 ) ( 8 ) ( 7 ) <- Player's 2 pits\n" +
s"( ) ( )\n" +
s" 13 6\n" +
s"( ) ( )\n" +
s" ( 0 ) ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) <- Player's 1 pits\n"

def showBoard(): String =
s"\n ( ${pits(12)} ) ( ${pits(11)} ) ( ${pits(10)} ) ( ${pits(9)} ) ( ${pits(8)} ) ( ${pits(7)} )\n" +
s"( ) ( )\n" +
s" ${pits(13)} ${pits(6)}\n" +
s"( ) ( )\n" +
s" ( ${pits(0)} ) ( ${pits(1)} ) ( ${pits(2)} ) ( ${pits(3)} ) ( ${pits(4)} ) ( ${pits(5)} )\n"

def showScore(): String =
"Player's 1 final score: " +pits(PLAYER_1_BASE_INDEX) +
"\nPlayer's 2 final score: " +pits(PLAYER_2_BASE_INDEX)

def showWinningPLayer(): String =
if pits(PLAYER_1_BASE_INDEX) == pits(PLAYER_2_BASE_INDEX) then "Remis!"
else if pits(PLAYER_1_BASE_INDEX) > pits(PLAYER_2_BASE_INDEX) then "Player 1 won!"
else "Player 2 won!"

def switchActivePlayer(): Unit =
if activePlayer == PLAYER_1_ID then activePlayer = PLAYER_2_ID
else activePlayer = PLAYER_1_ID

def onlyOneOption(): Int =
var counter = 0
var moveOption = -1
val offset = if activePlayer == PLAYER_1_ID then 0 else 7
for(i<- 0 to 5)
if pits(i + offset) != 0 then {counter += 1; moveOption = i + offset} else ()
if counter == 1 then moveOption else -1

def isMoveCorrect(pit: Int): Boolean =
(activePlayer == PLAYER_1_ID && pit >= PLAYER_1_START_INDEX && pit < PLAYER_1_BASE_INDEX && pits(pit) != 0) ||
(activePlayer == PLAYER_2_ID && pit >= PLAYER_2_START_INDEX && pit < PLAYER_2_BASE_INDEX && pits(pit) != 0)

def didPlayer1End(): Boolean =
activePlayer == 1 && pits.slice(PLAYER_1_START_INDEX, PLAYER_1_BASE_INDEX).sum == 0

def didPlayer2End(): Boolean =
activePlayer == 2 && pits.slice(PLAYER_2_START_INDEX, PLAYER_2_BASE_INDEX).sum == 0

def isGameFinished(): Boolean =
if didPlayer1End() then {finishedGameActionsPlayer2(); true}
else if didPlayer2End() then {finishedGameActionsPlayer1(); true}
else false

def finishedGameActionsPlayer1(): Unit =
pits(PLAYER_1_BASE_INDEX) += pits.slice(PLAYER_1_START_INDEX, PLAYER_1_BASE_INDEX).sum

def finishedGameActionsPlayer2(): Unit =
pits(PLAYER_2_BASE_INDEX) += pits.slice(PLAYER_2_START_INDEX, PLAYER_2_BASE_INDEX).sum

def endOfMoveInPlayersEmptyPit(endPit: Int): Unit =
val stolenStonesIndex = PLAYER_2_BASE_INDEX - 1 - endPit
if pits(stolenStonesIndex) == 0 then () else
if (activePlayer == PLAYER_1_ID) then
pits(PLAYER_1_BASE_INDEX) = pits(PLAYER_1_BASE_INDEX) + pits(stolenStonesIndex) + 1
else
pits(PLAYER_2_BASE_INDEX) = pits(PLAYER_2_BASE_INDEX) + pits(stolenStonesIndex) + 1
pits(stolenStonesIndex) = 0
pits(endPit) = 0

def moveStones(pit: Int, stones: Int): Boolean =
if stones > 0 then
if activePlayer == PLAYER_1_ID then
if pit == PLAYER_2_BASE_INDEX then
moveStones((pit + 1) % NUMBER_OF_PITS, stones)
else
pits(pit) += 1
moveStones((pit + 1) % NUMBER_OF_PITS, stones - 1)
else
if pit == PLAYER_1_BASE_INDEX then
moveStones((pit + 1) % NUMBER_OF_PITS, stones)
else
pits(pit) += 1
moveStones((pit + 1) % NUMBER_OF_PITS, stones - 1)
else
val lastPit = if pit != 0 then pit - 1 else PLAYER_2_BASE_INDEX
if activePlayer == 1 then
if lastPit == PLAYER_1_BASE_INDEX then false
else if lastPit >= PLAYER_1_START_INDEX && lastPit < PLAYER_1_BASE_INDEX && pits(lastPit) == 1 then
endOfMoveInPlayersEmptyPit(lastPit)
true
else true
else
if lastPit == PLAYER_2_BASE_INDEX then false
else if lastPit >= PLAYER_2_START_INDEX && lastPit < PLAYER_2_BASE_INDEX && pits(lastPit) == 1 then
endOfMoveInPlayersEmptyPit(lastPit)
true
else true

def parcelOutStones(pitChosenByPlayer: Int): Boolean =
val rememberHowManyStonesInPit = pits(pitChosenByPlayer)
pits(pitChosenByPlayer) = 0
moveStones(pitChosenByPlayer + 1, rememberHowManyStonesInPit)

def performMoveOfOnePlayer(pitChosenByPlayer: Int): Boolean =
if parcelOutStones(pitChosenByPlayer) then switchActivePlayer() else ()
if isGameFinished() then true else false

}
10 changes: 10 additions & 0 deletions Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package server
import server.ServerCommunication

object Main
{
def main(args: Array[String]) : Unit =
{
val serverCommunication = ServerCommunication
}
}
19 changes: 19 additions & 0 deletions Player.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package player
import akka.actor.{Actor, PoisonPill, actorRef2Scala}
import Player.{MoveStones, GameEnd}
import server.Server.PlayersMove

object Player
{
case class MoveStones()
case class GameEnd()
}

abstract class Player(val playerID: Int) extends Actor
{
def receive =
case MoveStones => sender() ! PlayersMove(playerID, makeMove())
case GameEnd => self ! PoisonPill

def makeMove(): Int
}
81 changes: 81 additions & 0 deletions Server.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package server
import akka.actor.{Actor, ActorRef, Kill, PoisonPill, Props, actorRef2Scala}
import akka.pattern.*
import akka.util.Timeout
import gameUtensils.KalahaBoard
import javax.swing.JLabel
import server.Server.{PlayersMove, GameOn, GameOff, Walkover}
import player.Player
import player.Player.{MoveStones, GameEnd}
import scala.concurrent.duration.*
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Random, Success}
import server.ServerCommunication.system

object Server
{
case class GameOn()
case class PlayersMove(val playersID: Int, val pit: Int)
case class Walkover()
case class GameOff()
}

class Server(private val kalahaBoard: KalahaBoard, private val player1: ActorRef, private val player2: ActorRef) extends Actor
{
implicit private val timeout: Timeout = Timeout(40.seconds)

println("Legend")
println(kalahaBoard.indexLegend())

def receive =
case GameOn => askPlayerAboutMove()
case GameOff => gameOffActions()
case Walkover => walkoverActions()

def askPlayerAboutMove(): Unit =
println("\nCurrent state of board: ")
println(kalahaBoard.showBoard())
println(s"\nPlayer's ${kalahaBoard.getActivePlayer()} turn!")

val player: ActorRef = whoIsPlaying()
val f = player ? MoveStones

f.onComplete {
case Success(PlayersMove(playerID, pitIndex)) =>
if kalahaBoard.isMoveCorrect(pitIndex) then
if kalahaBoard.performMoveOfOnePlayer(pitIndex) then self ! GameOff else self ! GameOn
else {println("That's not a correct move!"); self ! Walkover}

case Failure(exception) =>
println("You didn't make a move in requested time!")
system.stop(player)
self ! Walkover
}

def walkoverActions(): Unit =
println("\nWalkover!")

if kalahaBoard.getActivePlayer() == 1 then println("Player 2 won!")
else println("Player 1 won!")

endGame()

def gameOffActions(): Unit =
println("\nGame is finished!")
println(kalahaBoard.showScore())
println(kalahaBoard.showWinningPLayer())

endGame()

def endGame(): Unit =
player1 ! GameEnd
player2 ! GameEnd
self ! PoisonPill
println("\n\nGoodbye!")
System.exit(0)

def whoIsPlaying(): ActorRef =
if kalahaBoard.getActivePlayer() == 1 then player1
else player2

}
Loading