Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
sss.openstar.message.payloads.TicTacToeGameMessage.scala Maven / Gradle / Ivy
package sss.openstar.message.payloads
import sss.ancillary.Logging
import sss.openstar.UniqueNodeIdentifier
import sss.openstar.message.payloads.TicTacToeGameMessage.GameState
import sss.openstar.message.payloads.TicTacToeGameMessage.TicTacToeState.TicTacToeState
import sss.openstar.message.{MessageComposite, MessageUpdater}
object TicTacToeGameMessage {
private val maxMoves = 9
private val r1c1 = 0.toByte
private val r1c2 = 1.toByte
private val r1c3 = 2.toByte
private val r2c1 = 3.toByte
private val r2c2 = 4.toByte
private val r2c3 = 5.toByte
private val r3c1 = 6.toByte
private val r3c2 = 7.toByte
private val r3c3 = 8.toByte
private val winningCombos: Seq[Seq[Byte]] = Seq(
Seq(r1c1, r1c2, r1c3),
Seq(r2c1, r2c2, r2c3),
Seq(r3c1, r3c2, r3c3),
Seq(r1c1, r2c1, r3c1),
Seq(r1c2, r2c2, r3c2),
Seq(r1c3, r2c3, r3c3),
Seq(r1c1, r2c2, r3c3),
Seq(r3c1, r2c2, r1c3),
)
/**
* Could be generated from
* for {
* a <- Seq()
*/
val x = 1.toByte
val o = 0.toByte
object TicTacToeState extends Enumeration {
type TicTacToeState = Value
val notOver = Value(0)
val xWinner = Value(1)
val oWinner = Value(2)
val noWinner = Value(3)
}
private val initialState: Seq[Option[Byte]] = Seq.fill(maxMoves)(None)
case class GameState(boardState: Seq[Option[Byte]] = initialState) extends Logging {
require(boardState.size == maxMoves, s"Tic Tac Toe needs $maxMoves states, not ${boardState.size}")
lazy val numMoves: Int = boardState.foldLeft(0)((acc, e) => if (e.isDefined) acc + 1 else acc)
def checkCombo(winCombo: Seq[Byte]): Option[(TicTacToeState, Seq[Byte])] = {
(boardState(winCombo(0)), boardState(winCombo(1)), boardState(winCombo(2))) match {
case (Some(`o`), Some(`o`), Some(`o`)) => Some(TicTacToeState.oWinner, winCombo)
case (Some(`x`), Some(`x`), Some(`x`)) => Some(TicTacToeState.xWinner, winCombo)
case _ => None
}
}
val state: (TicTacToeState, Option[Seq[Byte]]) = {
winningCombos.view.flatMap(checkCombo).headOption match {
case None =>
if (numMoves == maxMoves) (TicTacToeState.noWinner, None)
else (TicTacToeState.notOver, None)
case Some((winner, winningLine)) => (winner, Some(winningLine))
}
}
def apply(nextState: GameState): GameState = {
if (state._1 == TicTacToeState.notOver) {
var moveCount = 0
val isGoodMove = boardState zip nextState.boardState forall {
case (Some(s1), Some(s2)) if s1 == s2 => true
case (None, None) => true
case (Some(s1), _) => false // cannot change a move
case (None, Some(_)) if moveCount <= 1 =>
//this won't catch a move made *for* you by an opponent
moveCount += 1
true
case _ => ???
}
if (isGoodMove) nextState
else {
log.error(s"Foiled attempted cheating move.")
this
}
} else {
log.error(s"Attempted move on already ended game.")
this
}
}
}
}
case class TicTacToeGameMessage(originator: UniqueNodeIdentifier,
opponent: UniqueNodeIdentifier,
moverSig: Array[Byte],
nextMover: UniqueNodeIdentifier,
gameState: GameState
) extends MessageComposite
with Logging {
private def isSigOfExpectedMover(sig: Array[Byte], identifier: UniqueNodeIdentifier): Boolean = true
override def apply[C >: MessageComposite](m: MessageUpdater): C = m match {
case tic@TicTacToeGameMessage(orig, oppo, mvrSg, nxtMvr, state) =>
if (originator == orig && oppo == opponent) {
if (isSigOfExpectedMover(mvrSg, nextMover)) {
if ((nextMover == opponent && nxtMvr == originator) ||
(nextMover == originator && nxtMvr == opponent)) {
copy(gameState = gameState(state), nextMover = nxtMvr)
} else {
log.error(s"This TicTacToe has a bad next mover $nextMover, reject move")
this
}
} else {
log.error(s"This TicTacToe has a bad sig, not from $nextMover, reject move")
this
}
} else {
log.error(s"This TicTacToe has $originator,$opponent and that's not compatible with $orig, $oppo")
this
}
case x =>
log.error(s"TicTacToecould not merge ${x.getClass}")
this
}
}