rearchitectured, simple AI for code finding
This commit is contained in:
parent
46d70661c3
commit
0fdb836e82
8 changed files with 389 additions and 205 deletions
|
@ -1,21 +1,45 @@
|
||||||
package bot
|
package bot
|
||||||
|
|
||||||
import scala.io.StdIn
|
import scala.io.StdIn
|
||||||
|
import DebugUtil.{log,dbg,time}
|
||||||
package object bot {
|
|
||||||
val Dbg: Boolean = true
|
|
||||||
val Log: Boolean = true
|
|
||||||
|
|
||||||
def dbg(msg: String) = if(Dbg) Console.err.println(msg)
|
|
||||||
def log(msg: String) = if(Log) Console.err.println(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
object Main {
|
object Main {
|
||||||
def main(args: Array[String]) = {
|
def main(args: Array[String]) = {
|
||||||
var input = StdIn.readLine()
|
|
||||||
|
|
||||||
while(input != null){
|
var input: String = ""
|
||||||
Parser.dispatch(input) foreach (println(_))
|
|
||||||
|
//Initialize general game settings
|
||||||
|
input = StdIn.readLine()
|
||||||
|
while (input startsWith "settings") {
|
||||||
|
S parse input
|
||||||
|
input = StdIn.readLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
log("init current state")
|
||||||
|
val currentState = new State()
|
||||||
|
log("init cortex")
|
||||||
|
val cortex = new Cortex()
|
||||||
|
|
||||||
|
log("init debug")
|
||||||
|
DebugUtil.state = Some(currentState)
|
||||||
|
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (input != null ){
|
||||||
|
def cmd: Seq[String] = input.split(" ")
|
||||||
|
|
||||||
|
// dispatch
|
||||||
|
cmd match {
|
||||||
|
case Seq("update", _*) => currentState parse input
|
||||||
|
case Seq("action", "character", _) => println ("bixiette")
|
||||||
|
case Seq("action", "move", t) => {
|
||||||
|
log(s"Timebank: $t")
|
||||||
|
val move: Direction = cortex move (currentState, t.toInt)
|
||||||
|
log (move.label)
|
||||||
|
println (move.label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input = StdIn.readLine()
|
input = StdIn.readLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package bot
|
|
||||||
|
|
||||||
import scala.util.Random
|
|
||||||
import bot.dbg
|
|
||||||
|
|
||||||
object Brain {
|
|
||||||
def move(time: Int): String = {
|
|
||||||
val dirs = Array("left", "right", "up", "down")
|
|
||||||
val dir = Random.nextInt(4)
|
|
||||||
|
|
||||||
dirs(dir)
|
|
||||||
}
|
|
||||||
}
|
|
152
bofa/src/main/scala/bot/Cortex.scala
Normal file
152
bofa/src/main/scala/bot/Cortex.scala
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import scala.util.Random
|
||||||
|
import DebugUtil.{ log, dbg, time }
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
sealed trait Direction {
|
||||||
|
val label: String
|
||||||
|
val deltaX: Int
|
||||||
|
val deltaY: Int
|
||||||
|
override def toString: String = label
|
||||||
|
}
|
||||||
|
final case object Left extends { val label = "left"; val deltaX = -1; val deltaY = 0 } with Direction
|
||||||
|
final case object Right extends { val label = "right"; val deltaX = 1; val deltaY = 0 } with Direction
|
||||||
|
final case object Up extends { val label = "up"; val deltaX = 0; val deltaY = -1 } with Direction
|
||||||
|
final case object Down extends { val label = "down"; val deltaX = 0; val deltaY = 1 } with Direction
|
||||||
|
|
||||||
|
class Cortex {
|
||||||
|
def move(state: State, time: Int): Direction = {
|
||||||
|
val directions: Seq[Direction] = possibleDirections(state.bofaField, state)
|
||||||
|
val weightBoard = weightCodes(1000, decr, state)
|
||||||
|
val dir = getMaxDir(state.bofaField, directions, weightBoard, state)
|
||||||
|
weightBoard(state.bofaField.x)(state.bofaField.y) = -999
|
||||||
|
printBoard(weightBoard)
|
||||||
|
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
|
def decr(dist: Int, start: Int) = {
|
||||||
|
(start/math.pow(2, (dist+1))).intValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
def getMaxField(weightBoard: Array[Array[Int]], state: State): Field = {
|
||||||
|
var maxField = (0,0)
|
||||||
|
for (x <- 0 until S.width; y <- 0 until S.height) {
|
||||||
|
if (weightBoard(x)(y) > weightBoard(maxField _1)(maxField _2)) maxField = (x,y)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.board(maxField _1)(maxField _2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def shortestPathTo(field: Field, state: State) = {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
def getMaxDir(field: Field, directions: Seq[Direction], weightBoard: Array[Array[Int]], state: State): Direction = {
|
||||||
|
var maxDir: Direction = randomDirection(directions)
|
||||||
|
val board = state.board
|
||||||
|
|
||||||
|
for (d <- directions){
|
||||||
|
var (x,y) = (field.x+d.deltaX, field.y+d.deltaY)
|
||||||
|
if ( d.equals(Left) && board(field.x)(field.y).citizens.contains(Gate("l")) ) x = S.width-1
|
||||||
|
else if ( d.equals(Right) && board(field.x)(field.y).citizens.contains(Gate("r")) ) x = 0
|
||||||
|
|
||||||
|
if (weightBoard(x)(y) > weightBoard(field.x+maxDir.deltaX)(field.y+maxDir.deltaY)) maxDir = d
|
||||||
|
}
|
||||||
|
|
||||||
|
maxDir
|
||||||
|
}
|
||||||
|
|
||||||
|
def possibleDirections(field: Field, state: State): Seq[Direction] = {
|
||||||
|
val pd = new mutable.ListBuffer[Direction]
|
||||||
|
val board = state.board
|
||||||
|
|
||||||
|
val toCheck = checkBorder(field, state)
|
||||||
|
|
||||||
|
for (d <- toCheck) {
|
||||||
|
if ( d == Left && board(field.x)(field.y).citizens.contains(Gate("l")) ) pd += d
|
||||||
|
else if ( d == Right && board(field.x)(field.y).citizens.contains(Gate("r")) ) pd += d
|
||||||
|
else {
|
||||||
|
val pfield = board(field.x + d.deltaX)(field.y + d.deltaY)
|
||||||
|
if (!(pfield.citizens contains Wall)) {
|
||||||
|
pd += d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pd
|
||||||
|
}
|
||||||
|
|
||||||
|
def weightCodes(start: Int, drop: (Int, Int) => Int, state: State): Array[Array[Int]] = {
|
||||||
|
val board = state.board
|
||||||
|
val codes = state.codes
|
||||||
|
val weightBoard = Array.ofDim[Int](S.width, S.height)
|
||||||
|
|
||||||
|
log(s"Codes: ${codes.length}")
|
||||||
|
|
||||||
|
|
||||||
|
for (code <- codes) {
|
||||||
|
val visited = Array.ofDim[Boolean](S.width, S.height)
|
||||||
|
for (x <- 0 until S.width; y <- 0 until S.height) visited(x)(y) = false
|
||||||
|
|
||||||
|
var coords: Seq[Field] = Seq.empty
|
||||||
|
val frontier = new mutable.ListBuffer[Field]
|
||||||
|
|
||||||
|
frontier += code
|
||||||
|
var weight = start
|
||||||
|
var dist = 0
|
||||||
|
|
||||||
|
while (weight >= drop(dist, start) && !frontier.isEmpty) {
|
||||||
|
coords = frontier.toList
|
||||||
|
frontier.clear()
|
||||||
|
|
||||||
|
for(field <- coords){
|
||||||
|
val dirs = possibleDirections(field, state)
|
||||||
|
for (d <- dirs) {
|
||||||
|
var (x,y) = (field.x+d.deltaX, field.y+d.deltaY)
|
||||||
|
if ( d == Left && board(field.x)(field.y).citizens.contains(Gate("l")) ) x = S.width-1
|
||||||
|
else if ( d == Right && board(field.x)(field.y).citizens.contains(Gate("r")) ) x = 0
|
||||||
|
|
||||||
|
if (!visited(x)(y)) frontier += board(x)(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visited(field.x)(field.y)) weightBoard(field.x)(field.y) += weight
|
||||||
|
visited(field.x)(field.y) = true
|
||||||
|
}
|
||||||
|
|
||||||
|
weight -= drop(dist, start)
|
||||||
|
dist += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
weightBoard
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkBorder(field: Field, state: State): Seq[Direction] = {
|
||||||
|
val dirs = new mutable.ListBuffer[Direction]
|
||||||
|
|
||||||
|
if (field.y != 0) dirs += Up
|
||||||
|
if (field.y != (S.height-1)) dirs += Down
|
||||||
|
if (field.x != 0 || field.citizens.contains(Gate("l"))) dirs += Left
|
||||||
|
if (field.x != (S.width-1) || field.citizens.contains(Gate("r"))) dirs += Right
|
||||||
|
|
||||||
|
dirs
|
||||||
|
}
|
||||||
|
|
||||||
|
def randomDirection(directions: Seq[Direction]) = directions(Random.nextInt(directions.length))
|
||||||
|
|
||||||
|
def printBoard(board: Array[Array[Int]]) = {
|
||||||
|
val btp = board.transpose
|
||||||
|
val buf = new StringBuilder
|
||||||
|
buf append "\n+" + "-" * (S.width * 10) + "+" + "\n"
|
||||||
|
for (line <- btp) {
|
||||||
|
buf append s"""|${line.map(_.toString.padTo(4, ' ')).mkString("|")}|"""
|
||||||
|
buf append "\n+" + "-" * (S.width * 10) + "+" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
log(buf toString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
25
bofa/src/main/scala/bot/DebugUtil.scala
Normal file
25
bofa/src/main/scala/bot/DebugUtil.scala
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
object DebugUtil {
|
||||||
|
var state: Option[State] = None // we need this to get access to the current game round
|
||||||
|
val Dbg: Boolean = true
|
||||||
|
val Log: Boolean = true
|
||||||
|
val Time: Boolean = true
|
||||||
|
|
||||||
|
def dbg(msg: String) = if(Dbg) Console.err.println(s"$round $msg")
|
||||||
|
def log(msg: String) = if(Log) Console.err.println(s"$round $msg")
|
||||||
|
|
||||||
|
def round: String = if (state.isDefined) s"[${state.get.round}]" else ""
|
||||||
|
|
||||||
|
def time[R](name: String)(block: => R): R = {
|
||||||
|
if(Time){
|
||||||
|
val t0 = System.nanoTime()
|
||||||
|
val result = block // call-by-name
|
||||||
|
val t1 = System.nanoTime()
|
||||||
|
log(s"$name: " + (t1 - t0)/1000000.0 + "ms")
|
||||||
|
result
|
||||||
|
}else{
|
||||||
|
block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
bofa/src/main/scala/bot/Extracted.scala
Normal file
5
bofa/src/main/scala/bot/Extracted.scala
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
trait Extracted
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
package bot
|
|
||||||
|
|
||||||
import bot.{dbg,log}
|
|
||||||
|
|
||||||
object Parser {
|
|
||||||
|
|
||||||
def dispatch(input: String): Option[String] = {
|
|
||||||
val parts: Seq[String] = input.split(" ")
|
|
||||||
|
|
||||||
parts match {
|
|
||||||
case Seq("settings", xs@_*) => settings(xs); None
|
|
||||||
case Seq("update", xs@_*) => update(xs); None
|
|
||||||
case Seq("action", "character", _) => Some("bixiette")
|
|
||||||
case Seq("action", "move", time) => Some(Brain.move(time.toInt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def settings(setting: Seq[String]): Unit = {
|
|
||||||
val splitNames = setting(1).split(",")
|
|
||||||
|
|
||||||
setting(0) match {
|
|
||||||
case "timebank" => Settings.timebank = setting(1).toInt
|
|
||||||
case "time_per_move" => Settings.timePerMove = setting(1).toInt
|
|
||||||
case "your_botid" => Settings.bofaId = setting(1).toInt
|
|
||||||
case "field_width" => Settings.width = setting(1).toInt
|
|
||||||
case "field_height" => Settings.height = setting(1).toInt
|
|
||||||
case "max_rounds" => Settings.maxRounds = setting(1).toInt
|
|
||||||
|
|
||||||
case "your_bot" => Settings.bofaName = setting(1)
|
|
||||||
case "player_names" => Settings.player1Name = splitNames(0)
|
|
||||||
Settings.player2Name = splitNames(1)
|
|
||||||
// case _ => let MatchError through, don't try recovering
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def update(update: Seq[String]): Unit = {
|
|
||||||
val cs = CurrentState
|
|
||||||
|
|
||||||
update match {
|
|
||||||
case Seq("game", "round", newround) => cs.round = newround.toInt
|
|
||||||
case Seq("game", "field", cellstring) => cells(cellstring)
|
|
||||||
case Seq(playerName, "snippets", snippets) => cs.playerBy(playerName).snippets = snippets.toInt
|
|
||||||
case Seq(playerName, "bombs", bombs) => cs.playerBy(playerName).bombs = bombs.toInt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def cells(cellstring: String): Unit = {
|
|
||||||
val cellParts = cellstring.split(",")
|
|
||||||
val board = CurrentState.board
|
|
||||||
|
|
||||||
for(cellId <- 0 until cellParts.length) {
|
|
||||||
val (x: Int, y: Int) = (cellId % Settings.width, cellId / Settings.width)
|
|
||||||
val cellCmd = cellParts(cellId).toCharArray()
|
|
||||||
|
|
||||||
cellCmd(0) match {
|
|
||||||
case '.' => board.put(EmptyCell(x,y))
|
|
||||||
case 'x' => board.put(Wall(x,y))
|
|
||||||
case 'P' => board.put(PlayerPos(x,y, cellCmd(1).toInt))
|
|
||||||
case 'G' => board.put(Gate(x,y, cellCmd(1).toString()))
|
|
||||||
case 'E' => board.put(BugPos(x,y, cellCmd(1).toInt))
|
|
||||||
case 'C' => board.put(CodePos(x,y))
|
|
||||||
case 'S' => {
|
|
||||||
if(cellCmd.length == 2) board.put(BugSpawner(x,y, Some(cellCmd(1).toInt)))
|
|
||||||
else board.put(BugSpawner(x,y, None))
|
|
||||||
}
|
|
||||||
case 'B' => {
|
|
||||||
if(cellCmd.length == 2) board.put(BombPos(x,y, Some(cellCmd(1).toInt)))
|
|
||||||
else board.put(BombPos(x,y, None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
46
bofa/src/main/scala/bot/Settings.scala
Normal file
46
bofa/src/main/scala/bot/Settings.scala
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import DebugUtil.{log,dbg,time}
|
||||||
|
|
||||||
|
/** Settings holder containing global settings set in the beginning. Only set once per game. */
|
||||||
|
object S {
|
||||||
|
var timebank: Int = -1
|
||||||
|
var timePerMove: Int = -1
|
||||||
|
var width: Int = -1
|
||||||
|
var height: Int = -1
|
||||||
|
var maxRounds: Int = -1
|
||||||
|
var players: Seq[String] = Seq()
|
||||||
|
var bofaId: Int = -1
|
||||||
|
var bofaName: String = ""
|
||||||
|
|
||||||
|
def oppoId = 1-bofaId
|
||||||
|
def oppoName = players(oppoId)
|
||||||
|
|
||||||
|
def parse(setting: String): Unit = {
|
||||||
|
val parts = setting.split(" ")
|
||||||
|
|
||||||
|
parts(1) match {
|
||||||
|
case "timebank" => timebank = parts(2).toInt
|
||||||
|
case "time_per_move" => timePerMove = parts(2).toInt
|
||||||
|
case "your_botid" => bofaId = parts(2).toInt
|
||||||
|
case "field_width" => width = parts(2).toInt
|
||||||
|
case "field_height" => height = parts(2).toInt
|
||||||
|
case "max_rounds" => maxRounds = parts(2).toInt
|
||||||
|
case "your_bot" => bofaName = parts(2)
|
||||||
|
case "player_names" => players = parts(2).split(",")
|
||||||
|
// case _ => let MatchError through, don't try recovering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString: String = {
|
||||||
|
s"""
|
||||||
|
Settings {
|
||||||
|
.timebank = $timebank
|
||||||
|
.timePerMove = $timePerMove
|
||||||
|
.width = $width
|
||||||
|
.height = $height
|
||||||
|
.maxRounds = $maxRounds
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,123 +1,142 @@
|
||||||
package bot
|
package bot
|
||||||
|
|
||||||
import bot.dbg
|
import scala.collection.Seq
|
||||||
|
import DebugUtil.{ log, dbg, time }
|
||||||
|
|
||||||
class Field(val x: Int, val y: Int){
|
/** Field of the game board
|
||||||
override def toString: String = s"Field { .x = $x, .y = $y }"
|
*
|
||||||
|
* The field knows its citizens and location in the world
|
||||||
|
* for faster access
|
||||||
|
*/
|
||||||
|
class Field(val x: Int, val y: Int, var citizens: Seq[Citizen]) {
|
||||||
|
def this(x: Int, y: Int) = this(x, y, Seq.empty)
|
||||||
|
|
||||||
|
override def toString(): String = s"""($x,$y): ${citizens.map(_.getClass.getSimpleName).mkString(",")}"""
|
||||||
|
}
|
||||||
|
object Field {
|
||||||
|
val empty: Field = new Field(-1, -1)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int): Field = new Field (x, y)
|
||||||
|
def apply(x: Int, y: Int, citizen: Seq[Citizen]): Field = Field (x, y, citizen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// static fields
|
/** Potential population of the game board */
|
||||||
case class EmptyCell (x0: Int, y0: Int) extends Field(x0,y0)
|
sealed trait Citizen
|
||||||
case class Wall (x0: Int, y0: Int) extends Field(x0,y0)
|
final case object Floor extends Citizen
|
||||||
case class Gate (x0: Int, y0: Int, direction: String) extends Field(x0,y0)
|
final case object Wall extends Citizen
|
||||||
case class BugSpawner (x0: Int, y0: Int, rounds: Option[Int]) extends Field(x0,y0)
|
final case object Code extends Citizen
|
||||||
// dynamic fields
|
final case class Gate (direction: String) extends Citizen
|
||||||
case class PlayerPos (x0: Int, y0: Int, id: Int) extends Field(x0,y0)
|
final case class Spawner (rounds: Option[Int]) extends Citizen
|
||||||
case class BugPos (x0: Int, y0: Int, ai: Int) extends Field(x0,y0)
|
final case class Bug (ai: Int) extends Citizen
|
||||||
case class BombPos (x0: Int, y0: Int, rounds: Option[Int]) extends Field(x0,y0)
|
final case class Bomb (rounds: Option[Int]) extends Citizen
|
||||||
case class CodePos (x0: Int, y0: Int) extends Field(x0,y0)
|
final case class Player (id: Int, name: String) extends Citizen {
|
||||||
|
|
||||||
|
|
||||||
class Player(id: Int){
|
|
||||||
val name: String = Settings.players(id)
|
|
||||||
var snippets: Int = 0
|
var snippets: Int = 0
|
||||||
var bombs: Int = 0
|
var bombs: Int = 0
|
||||||
|
|
||||||
override def toString = s"Player { .name = $name, .snippets = $snippets, .bombs = $bombs }"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Board {
|
/** A State in the game
|
||||||
// index top-left (0,0) to bottom-right (Settings.width,Settings.height)
|
*
|
||||||
private val _board: Array[Array[Field]] = Array.ofDim[Field](Settings.width, Settings.height)
|
* A state consists at minimum of a populated game board and two players.
|
||||||
|
*/
|
||||||
|
class State {
|
||||||
|
// index top-left (0,0) to bottom-right (S.width,S.height)
|
||||||
|
val board: Array[Array[Field]] = Array.ofDim[Field](S.width, S.height)
|
||||||
|
for (i <- board.indices; j <- board(i).indices) board(i)(j) = Field (i, j) // init board
|
||||||
|
|
||||||
def put(field: Field){
|
var round: Int = -1 // may not be set
|
||||||
_board(field.x)(field.y) = field
|
var bofa: Player = new Player(S.bofaId, S.bofaName)
|
||||||
|
var oppo: Player = new Player(S.oppoId, S.oppoName)
|
||||||
|
|
||||||
|
var bofaField: Field = Field.empty
|
||||||
|
var oppoField: Field = Field.empty
|
||||||
|
|
||||||
|
var codes: Seq[Field] = Seq.empty
|
||||||
|
var bombs: Seq[Field] = Seq.empty
|
||||||
|
var bugs: Seq[Field] = Seq.empty
|
||||||
|
|
||||||
|
def update(x: Int, y: Int, citizens: Seq[Citizen]) {
|
||||||
|
val field = board(x)(y)
|
||||||
|
field.citizens = citizens
|
||||||
|
|
||||||
|
for (citizen <- citizens) {
|
||||||
|
citizen match {
|
||||||
|
case Player(id, name) if id == S.bofaId => bofaField = field
|
||||||
|
case Player(id, name) if id == S.oppoId => oppoField = field
|
||||||
|
|
||||||
|
case Code => codes = codes :+ field
|
||||||
|
case Bomb (_) => bombs = bombs :+ field
|
||||||
|
case Bug (_) => bugs = bugs :+ field
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def playerBy(id: Int) = if (id == (S.bofaId)) bofa else oppo
|
||||||
|
def playerBy(name: String) = if (name equals (S.bofaName)) bofa else oppo
|
||||||
|
|
||||||
|
def parse(update: String): Unit = {
|
||||||
|
val cmd: Seq[String] = update split (" ") tail
|
||||||
|
|
||||||
|
cmd match {
|
||||||
|
case Seq("game", "round", uround) => round = uround.toInt
|
||||||
|
case Seq("game", "field", cellstring) => parseCells(cellstring)
|
||||||
|
case Seq(name, "snippets", snippets) => playerBy(name).snippets = snippets.toInt
|
||||||
|
case Seq(name, "bombs", bombs) => playerBy(name).bombs = bombs.toInt
|
||||||
|
// case _ => let MatchError through, don't try recovering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def parseCells(cellstring: String): Unit = {
|
||||||
|
codes = Seq.empty
|
||||||
|
bombs = Seq.empty
|
||||||
|
bugs = Seq.empty
|
||||||
|
|
||||||
|
val cellParts = cellstring.split(",")
|
||||||
|
|
||||||
|
for (cellId <- 0 until cellParts.length) {
|
||||||
|
val (x: Int, y: Int) = (cellId % S.width, cellId / S.width)
|
||||||
|
val citizens: Array[Citizen] =
|
||||||
|
cellParts(cellId).split(";")
|
||||||
|
.map(_.toCharArray())
|
||||||
|
.map {
|
||||||
|
case Array('.') => Floor
|
||||||
|
case Array('x') => Wall
|
||||||
|
case Array('P', id) => playerBy(id.asDigit)
|
||||||
|
case Array('G', direction) => Gate(direction.toString)
|
||||||
|
case Array('E', ai) => Bug(ai.asDigit)
|
||||||
|
case Array('C') => Code
|
||||||
|
case Array('S') => Spawner(None)
|
||||||
|
case Array('S', time @ _*) => Spawner(Some(time.mkString.toInt))
|
||||||
|
case Array('B') => Bomb(None)
|
||||||
|
case Array('B', time @ _*) => Bomb(Some(time.mkString.toInt))
|
||||||
|
}
|
||||||
|
|
||||||
|
update(x, y, citizens)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString = {
|
override def toString = {
|
||||||
val btp = _board.transpose
|
val btp = board.transpose
|
||||||
val buf = new StringBuilder
|
val buf = new StringBuilder
|
||||||
buf append "+" + "-" * (Settings.width*10+Settings.width*3-1) + "+" + "\n"
|
buf append "\n+" + "-" * (S.width * 10 + S.width * 3 - 1) + "+" + "\n"
|
||||||
for(j <- 0 until Settings.height){
|
for (line <- btp) {
|
||||||
buf append s"""| ${btp(j) map (_.getClass.getSimpleName().padTo(10, ' ')) mkString (" | ")} |\n"""
|
val maxCitizens = line map (_.citizens.length) max
|
||||||
|
|
||||||
|
for (citizenCounter <- 0 until maxCitizens) {
|
||||||
|
for (field <- line) {
|
||||||
|
if (field.citizens.length > citizenCounter)
|
||||||
|
buf append s"""| ${field.citizens(citizenCounter).getClass.getSimpleName().padTo(10, ' ')} """
|
||||||
|
else
|
||||||
|
buf append s"| ${" " * 10} "
|
||||||
|
}
|
||||||
|
buf append "|\n"
|
||||||
|
}
|
||||||
|
buf append "+" + "-" * (S.width * 10 + S.width * 3 - 1) + "+" + "\n"
|
||||||
}
|
}
|
||||||
buf append "+" + "-" * (Settings.width*10+Settings.width*3-1) + "+" + "\n"
|
|
||||||
buf toString
|
buf toString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class State {
|
|
||||||
val bofa: Player = new Player(Settings.bofaId)
|
|
||||||
val oppo: Player = new Player(2)
|
|
||||||
val board: Board = new Board()
|
|
||||||
|
|
||||||
def playerBy(name: String) = if (name equals bofa.name) bofa else oppo
|
|
||||||
}
|
|
||||||
|
|
||||||
object CurrentState extends State {
|
|
||||||
var round: Int = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Settings holder containing global settings set only once per game.
|
|
||||||
*
|
|
||||||
* These settings should not change after initialization and
|
|
||||||
* stay constant for the whole game.
|
|
||||||
*/
|
|
||||||
object Settings {
|
|
||||||
private var _timebank: Option[Int] = None
|
|
||||||
private var _timePerMove: Option[Int] = None
|
|
||||||
private var _player1Name: Option[String] = None
|
|
||||||
private var _player2Name: Option[String] = None
|
|
||||||
private var _bofaName: Option[String] = None
|
|
||||||
private var _bofaId: Option[Int] = None
|
|
||||||
private var _width: Option[Int] = None
|
|
||||||
private var _height: Option[Int] = None
|
|
||||||
private var _maxRounds: Option[Int] = None
|
|
||||||
|
|
||||||
|
|
||||||
/* Get Option or fail with exception
|
|
||||||
* Don't try to be smart but just fail if settings not initialized properly
|
|
||||||
*/
|
|
||||||
def timebank = _timebank.get
|
|
||||||
def timePerMove = _timePerMove.get
|
|
||||||
def player1Name = _player1Name.get
|
|
||||||
def player2Name = _player2Name.get
|
|
||||||
def bofaName = _bofaName.get
|
|
||||||
def bofaId = _bofaId.get
|
|
||||||
def width = _width.get
|
|
||||||
def height = _height.get
|
|
||||||
def maxRounds = _maxRounds.get
|
|
||||||
|
|
||||||
def timebank_= (value: Int) = _timebank = Some(value)
|
|
||||||
def timePerMove_= (value: Int) = _timePerMove = Some(value)
|
|
||||||
def player1Name_= (value: String) = _player1Name = Some(value)
|
|
||||||
def player2Name_= (value: String) = _player2Name = Some(value)
|
|
||||||
def bofaName_= (value: String) = _bofaName = Some(value)
|
|
||||||
def bofaId_= (value: Int) = _bofaId = Some(value)
|
|
||||||
def width_= (value: Int) = _width = Some(value)
|
|
||||||
def height_= (value: Int) = _height = Some(value)
|
|
||||||
def maxRounds_= (value: Int) = _maxRounds = Some(value)
|
|
||||||
|
|
||||||
def oppoName = if(bofaId == 1) player2Name else player1Name
|
|
||||||
def oppoId = if(bofaId == 1) 2 else 1
|
|
||||||
def players(id: Int) = if(id == 1) player1Name else player2Name
|
|
||||||
|
|
||||||
override def toString: String = {
|
|
||||||
s"""
|
|
||||||
Settings {
|
|
||||||
.timebank = ${_timebank.getOrElse("Not Set")}
|
|
||||||
.timePerMove = ${_timePerMove.getOrElse("Not Set")}
|
|
||||||
.player1Name = ${_player1Name.getOrElse("Not Set")}
|
|
||||||
.player2Name = ${_player2Name.getOrElse("Not Set")}
|
|
||||||
.bofaName = ${_bofaName.getOrElse("Not Set")}
|
|
||||||
.bofaId = ${_bofaId.getOrElse("Not Set")}
|
|
||||||
.width = ${_width.getOrElse("Not Set")}
|
|
||||||
.height = ${_height.getOrElse("Not Set")}
|
|
||||||
.maxRounds = ${_maxRounds.getOrElse("Not Set")}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue