diff --git a/AIPlayer.scala b/AIPlayer.scala new file mode 100644 index 00000000..c8202134 --- /dev/null +++ b/AIPlayer.scala @@ -0,0 +1,15 @@ +class AIPlayer(private val id: Int, private val game: Kalaha) extends Player(id) { + + private val logicEngine = LogicEngine(game, id) + + override def getId: Int = id + + override def makeMove(time: Int): Int = { + + logicEngine.makeMove(time: Int) + + } + + override def getEnemyMove(move: Int): Unit = logicEngine.updateEnemyMove(move) + +} diff --git a/Kalaha.scala b/Kalaha.scala new file mode 100644 index 00000000..a3796c38 --- /dev/null +++ b/Kalaha.scala @@ -0,0 +1,105 @@ +import scala.util.control.Breaks.break + +class Kalaha(private val field1: Array[Int], private val field2: Array[Int]) { + + def this() = { + + this(Array(6,6,6,6,6,6,0),Array(6,6,6,6,6,6,0)) + + } + + def move(id: Int, field: Int): Int ={ + + var level = id == 1 + var tmpField = nextField(level) + var points = tmpField(field) + tmpField(field) = 0 + var index = field + while(points > 0 && index <= 6){ + index += 1 + if index == 7 then { + index = 0 + level = !level + tmpField = nextField(level) + } + tmpField(index) += 1 + points -= 1 + } + + if (id == 1) == level && index <= 5 then + val otherField = nextField(!level) + if tmpField(index) == 1 then + tmpField(6) += otherField(5 - index) + otherField(5-index) = 0 + + if index == 6 then + if (id == 1) == level then + if level then 1 else 2 + else + if level then 1 else 2 + else + if id == 1 then 2 else 1 + } + + def getScore(id: Int): Int = + if id == 1 then field1(6) - field2(6) + else field2(6) - field1(6) + + def isOver():Boolean = { + + var isOver1 = true + var isOver2 = true + var i = 0 + while(i < 6){ + if field1(i) != 0 then isOver1 = false + i += 1 + } + i = 0 + while(i < 6){ + if field2(i) != 0 then isOver2 = false + i += 1 + } + isOver1 || isOver2 + } + + def whoWon():Int = { + + var res = 0 + val res1 = field1.foldLeft(0)((x, sum) => sum + x) + val res2 = field2.foldLeft(0)((x, sum) => sum + x) + if res1 > res2 then res = 1 + else if res1 < res2 then res = 2 + res + } + + def printFields(id: Int): Unit = { + + var playerField = field1 + var enemyField = field2.reverse + + if id == 2 then + playerField = field2 + enemyField = field1.reverse + + println("|---------------Enemy Field---------------|") + println("|Base |--6--|--5--|--4--|--3--|--2--|--1--|") + enemyField.foreach(x => printf("|%5d",x)) + println("|") + println("|-----------------------------------------|") + playerField.foreach(x => printf("|%5d",x)) + println("|") + println("|--1--|--2--|--3--|--4--|--5--|--6--| Base|") + println("|--------------Player Field---------------|") + } + + def copy(): Kalaha = new Kalaha(field1.clone(), field2.clone()) + + def getField(id: Int): Array[Int] = + if id == 1 then field1 + else field2 + + //val nextField1: Int => Array[Int] = id => if id == 1 then field1 else field2 + + val nextField: Boolean => Array[Int] = value =>if value then field1 else field2 + +} diff --git a/LogicEngine.scala b/LogicEngine.scala new file mode 100644 index 00000000..2b68deaa --- /dev/null +++ b/LogicEngine.scala @@ -0,0 +1,105 @@ +import scala.annotation.tailrec +import scala.concurrent.{Await, Future} +import scala.concurrent.duration.* +import scala.concurrent.ExecutionContext.Implicits.global + +class LogicEngine(val game: Kalaha, val playerId: Int) { + + var moves: List[Int] = List[Int]() + val decisions = new MyTree(6, game) + var bestMove: Int = 0 + + def updateEnemyMove(move: Int): Unit = moves = moves :+ move + + def makeMove(time: Int): Int = { + val start = System.currentTimeMillis() + if decisions.getRoot.getValue._3 == 0 then + if moves.isEmpty then decisions.getRoot.setValue(decisions.getRoot.getValue._1, decisions.getRoot.getValue._2, playerId) + else { + decisions.getRoot.setValue(game.getScore(playerId), game, playerId) + moves = List() + } + var root = decisions.getRoot + moves.foreach(move =>{ + if !root.hasKids then { + fillChildren() + root = root.getKids(move) + } + else root = root.getKids(move) + + }) + moves = List() + simulate(time*1000, start) + decisions.setRoot(decisions.getRoot.getKids(bestMove)) + bestMove + } + + def fillChildren(): Unit ={ + @tailrec + def fillKid(curr: Node, restOfMoves: List[Int]):Unit = + restOfMoves match { + case List() => () + case h::t => + simulateIteration(h, curr, curr.getValue._3) + fillKid(curr.getKids(h),t) + } + + + } + + def updateBestMove(): Unit = + var choiceMove = (0,0) + var bestScore = -100 + var i = 0 + decisions.getRoot.bestChoice(playerId).foreach((res, next) =>{ + if game.getField(playerId)(i) != 0 then { + if res > bestScore then + bestScore = res + choiceMove = (i, next) + else if choiceMove._2 != playerId && res == bestScore && next == playerId then + bestScore = res + choiceMove = (i, next) + } + i += 1 + }) + bestMove = choiceMove._1 + + def simulate(time: Int, start: Long): Unit = + + def simulateMove(curr: Node): Unit = + if (System.currentTimeMillis() - start) < (time* 3 / 4) then { + if curr.hasKids then + curr.getKids.foreach(kid =>{ + simulateMove(kid) + }) + else + val f1 = Future(simulateIteration(0,curr, curr.getValue._3)) + val f2 = Future(simulateIteration(1,curr, curr.getValue._3)) + val f3 = Future(simulateIteration(2,curr, curr.getValue._3)) + val f4 = Future(simulateIteration(3,curr, curr.getValue._3)) + val f5 = Future(simulateIteration(4,curr, curr.getValue._3)) + val f6 = Future(simulateIteration(5,curr, curr.getValue._3)) + if ((System.currentTimeMillis() - start) < (time * 4 / 5)) { + Await.result(f1, Duration.Inf) + Await.result(f2, Duration.Inf) + Await.result(f3, Duration.Inf) + Await.result(f4, Duration.Inf) + Await.result(f5, Duration.Inf) + Await.result(f6, Duration.Inf) + } + else () + } + while((System.currentTimeMillis() - start) < (time* 4 / 5)){ + simulateMove(decisions.getRoot) + decisions.getRoot.countSubtree() + updateBestMove() + } + + def simulateIteration(fieldNumber: Int, curr: Node, currPlayerId: Int): Unit = { + val simulationGame = curr.getValue._2.copy() + val whoseNext = simulationGame.move(currPlayerId, fieldNumber) + curr.addNewKidAt((simulationGame.getScore(playerId), simulationGame, whoseNext), fieldNumber) + } + + def showTree(): Unit = decisions.print() +} diff --git a/Main.scala b/Main.scala new file mode 100644 index 00000000..b22f16f3 --- /dev/null +++ b/Main.scala @@ -0,0 +1,9 @@ +object Main { + def main(args: Array[String]): Unit = { + + val menu = new Menu() + menu.run() + + + } +} diff --git a/Menu.scala b/Menu.scala new file mode 100644 index 00000000..23d7c96f --- /dev/null +++ b/Menu.scala @@ -0,0 +1,54 @@ +import scala.io.StdIn.* + +class Menu() { + + val time = 30 + + def run(): Unit = + val game = new Kalaha() + println("KALAHA") + println("1.Singleplayer") + println("2.Multiplayer") + println("3.Simulation") + println("4.Exit") + print("Choice: ") + + var choice = readInt() + + if choice == 1 then + choice = difficulty(game) + if choice == 2 then + val p1 = new Player(1) + val p2 = new Player(2) + val server = new Server(p1, p2, game, time) + choice = server.play() + if choice == 3 then + val p1 = new AIPlayer(1, game) + val p2 = new AIPlayer(2, game) + val server = new Server(p1, p2, game, time) + choice = server.play() + if choice != 4 then run() + + def difficulty(game: Kalaha): Int = + println("Select difficulty level:") + println("1.Easy") + println("2.Hard") + print("Choice: ") + + var choice = readInt() + + if choice == 1 then + val p1 = new Player(1) + val p2 = new RandomPlayer(2, game) + val server = new Server(p1, p2, game, time) + println("You are Player 1") + choice = server.play() + if choice == 2 then + val p1 = new Player(1) + val p2 = new AIPlayer(2, game) + val server = new Server(p1, p2, game, time) + println("You are Player 1") + choice = server.play() + + choice +} diff --git a/Player.scala b/Player.scala new file mode 100644 index 00000000..8cfb5d3a --- /dev/null +++ b/Player.scala @@ -0,0 +1,15 @@ +import scala.io.StdIn.* + +class Player(private val id: Int) { + + def makeMove(time: Int): Int ={ + + print("\nSelect field number: ") + val field = readInt() + field - 1 + } + + def getId: Int = id + + def getEnemyMove(move: Int): Unit = () +} diff --git a/RandomPlayer.scala b/RandomPlayer.scala new file mode 100644 index 00000000..c28d4a8e --- /dev/null +++ b/RandomPlayer.scala @@ -0,0 +1,22 @@ +import scala.io.StdIn.readInt +import scala.util.Random + +class RandomPlayer(private val id: Int, private val game: Kalaha) extends Player(id) { + + override def makeMove(time: Int): Int ={ + + var isOk = false + var choice = 0 + while(!isOk){ + + choice = Random.nextInt(6) + if game.getField(id)(choice) != 0 then isOk = true + + } + choice + } + + override def getId: Int = id + + override def getEnemyMove(move: Int): Unit = () +} diff --git a/Server.scala b/Server.scala new file mode 100644 index 00000000..d92fc219 --- /dev/null +++ b/Server.scala @@ -0,0 +1,48 @@ +import scala.util.Random +import scala.concurrent.{Await, Future} +import scala.concurrent.duration._ +import scala.concurrent.ExecutionContext.Implicits.global +import java.util.concurrent.TimeUnit + +class Server(private val player1: Player,private val player2: Player,private val game: Kalaha,private val time: Int): + + def play(): Int = + + var movingPlayer:Player = player1 + var next = 1 + val rng = Random.nextInt() + if rng % 2 == 1 then movingPlayer = player2 + next = 2 + + while(!game.isOver() ){ + println("Player "+ movingPlayer.getId + " moving") + game.printFields(movingPlayer.getId) + try{ + val FutureChoice = Future(movingPlayer.makeMove(time)) + val choice = Await.result(FutureChoice,Duration(time, TimeUnit.SECONDS)) + + if choice < 0 || choice > 5 then + println(choice + " is a wrong move! You loose your turn") + if next == 1 then next = 2 + else next = 1 + else + if next == 1 then player2.getEnemyMove(choice) + else player1.getEnemyMove(choice) + next = game.move(movingPlayer.getId, choice) + println("Field " + (choice + 1) + " chosen") + } + catch{ + case e:java.util.concurrent.TimeoutException => println("Czas minal!") + return 4 + + } + if next == 1 then movingPlayer = player1 + else movingPlayer = player2 + + } + game.printFields(movingPlayer.getId) + val winner = game.whoWon() + println("GAME IS OVER!") + if winner == 0 then println("It is a draw!!!\nCONGRATULATION") + else println("\nand the winner is...\nPlayer " + winner + "!!!") + 0 diff --git a/Tree.scala b/Tree.scala new file mode 100644 index 00000000..8a1988d3 --- /dev/null +++ b/Tree.scala @@ -0,0 +1,172 @@ +import scala.annotation.tailrec + +def aToPowerb(a: Int, b:Int): Int = + @tailrec + def iterator(result: Int, rest: Int): Int = + rest match + case 1 => result + case 0 => 1 + case _ => iterator(result*a, rest - 1) + iterator(a, b) + +class Node(var value: (Int, Kalaha, Int), val size: Int) { + + private val kids = new Array[Node](size) + private var index = 0 + + def getValue:(Int, Kalaha, Int) = value + + def setValue(newValue: (Int, Kalaha, Int)): Unit = value = newValue + + def hasKids: Boolean = index != 0 + + def getKids: Array[Node] = kids + + def getIndex: Int = index + + def hasAllKids: Boolean = index == size + + def addKid(newKid: Node): Unit = + kids(index) = newKid + if index == -1 then index = 1 + else index += 1 + + def addNewKid(value: (Int, Kalaha, Int)): Unit = + kids(index) = new Node(value, kids.length) + if index == -1 then index = 1 + else index += 1 + + def addKidAt(newKid: Node, index: Int): Unit = + kids(index) = newKid + if this.index == 0 then this.index = -1 + + def addNewKidAt(value: (Int, Kalaha, Int), index: Int): Unit = + kids(index) = new Node(value, kids.length) + if this.index == 0 then this.index = -1 + + def findPaths(): Array[((Int, Kalaha, Int), List[Int])] = + var res = new Array[((Int, Kalaha, Int), List[Int])](0) + def search(curr: Node, path: List[Int]): Unit = + if curr.hasKids then + var i = 0 + while(i < size){ + res = res:+(curr.getKids(i).getValue, path:+i) + search(curr.getKids(i), path:+i) + i += 1 + } + search(this,List()) + res + + def maxKid(): Int = + var index = 0 + var i = 0 + var maxValue = kids(0).value._1 + while (i < kids.length){ + + if kids(i).value._1 > maxValue then { + index = i + maxValue = kids(i).value._1 + } + i += 1 + } + index + + def minKid(): Int = + var index = 0 + var i = 0 + var minValue = kids(0).value._1 + while (i < kids.length){ + + if kids(i).value._1 < minValue then { + index = i + minValue = kids(i).value._1 + } + i += 1 + + } + index + + def chooseBest(mine: Boolean): Int = + if mine then maxKid() + else minKid() + + def countNodes(): Int = + @tailrec + def count(stack: List[Node], res: Int): Int = + stack match { + case List() => res + case h::t => + if h.hasKids then count(h.kids.toList:::t, res+1) + else count(t, res+1) + } + count(List(this), 0) + + def fullHeight(): Int = + val nodesAmount = countNodes() + @tailrec + def counter(rest: Int, power: Int): Int = + if rest < 0 then power - 1 + else counter(rest - aToPowerb(size, power), power + 1) + counter(nodesAmount,0) + + def bestChoice(playerId: Int): Array[(Int, Int)] = + val res = Array.fill(6)((0, 0)) + def iterator(curr: Node, index: Int, depth: Int): Unit = + if curr.hasKids && depth >= 0 then { + res(index) = (curr.chooseBest(curr.value._3 == playerId), curr.value._3) + curr.kids.foreach(kid => { + iterator(kid, index, depth - 1) + }) + } + var i = 0 + val depth: Int = fullHeight() + kids.foreach(kid => { + iterator(kid, i, depth - 2) + i += 1 + }) + res + + def countSubtree(): Unit = + def kidCount(): Int = + var res = 0 + if hasAllKids then + kids.foreach(kid => { + kid.countSubtree() + res = res + kid.value._1 + }) + res / kids.length + value = (value._1 + kidCount(), value._2, value._3) + + def print(): Unit = printf("%d",value._1) +} + +class MyTree(val kidsLimit: Int, game: Kalaha){ + + var root: Node = Node((0, game, 0), kidsLimit) + + def getRoot: Node = root + + def setRoot(newRoot: Node): Unit = root = newRoot + + def getNode(path: List[Int]): Node = + @tailrec + def iterate(curr: Node, tail: List[Int]): Node = + if !curr.hasKids then curr + else tail match + case Nil => curr + case h::t => iterate(curr.getKids(h), t) + iterate(root, path) + + def print(): Unit = + @tailrec + def iterator(queue: List[Node]): Unit ={ + queue match { + case List() => () + case h::t => + printf("%d ",h.getValue._1) + iterator(t:::h.getKids.toList) + } + } + iterator(List(root)) +} +