MsHackman parser in Scala
This commit is contained in:
commit
a429ffa000
10 changed files with 355 additions and 0 deletions
96
.gitignore
vendored
Normal file
96
.gitignore
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/sbt,linux,scala,eclipse
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
### Linux ###
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
### SBT ###
|
||||||
|
# Simple Build Tool
|
||||||
|
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
|
||||||
|
|
||||||
|
dist/*
|
||||||
|
target/
|
||||||
|
lib_managed/
|
||||||
|
src_managed/
|
||||||
|
project/boot/
|
||||||
|
project/plugins/project/
|
||||||
|
.history
|
||||||
|
.cache
|
||||||
|
.lib/
|
||||||
|
|
||||||
|
### Scala ###
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/sbt,linux,scala,eclipse
|
BIN
bofa/.cache-tests
Normal file
BIN
bofa/.cache-tests
Normal file
Binary file not shown.
12
bofa/build.sbt
Normal file
12
bofa/build.sbt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import Dependencies._
|
||||||
|
|
||||||
|
lazy val root = (project in file(".")).
|
||||||
|
settings(
|
||||||
|
inThisBuild(List(
|
||||||
|
organization := "com.example",
|
||||||
|
scalaVersion := "2.12.3",
|
||||||
|
version := "0.1.0-SNAPSHOT"
|
||||||
|
)),
|
||||||
|
name := "Bofa",
|
||||||
|
libraryDependencies += scalaTest % Test
|
||||||
|
)
|
5
bofa/project/Dependencies.scala
Normal file
5
bofa/project/Dependencies.scala
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import sbt._
|
||||||
|
|
||||||
|
object Dependencies {
|
||||||
|
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.3"
|
||||||
|
}
|
1
bofa/project/build.properties
Normal file
1
bofa/project/build.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
sbt.version=1.0.2
|
2
bofa/project/plugins.sbt
Normal file
2
bofa/project/plugins.sbt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.3")
|
||||||
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
|
22
bofa/src/main/scala/bot/Bofa.scala
Normal file
22
bofa/src/main/scala/bot/Bofa.scala
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import scala.io.StdIn
|
||||||
|
|
||||||
|
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 {
|
||||||
|
def main(args: Array[String]) = {
|
||||||
|
var input = StdIn.readLine()
|
||||||
|
|
||||||
|
while(input != null){
|
||||||
|
Parser.dispatch(input)
|
||||||
|
input = StdIn.readLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
bofa/src/main/scala/bot/Brain.scala
Normal file
18
bofa/src/main/scala/bot/Brain.scala
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import scala.util.Random
|
||||||
|
import bot.dbg
|
||||||
|
|
||||||
|
object Brain {
|
||||||
|
def move(in: Int): String = {
|
||||||
|
val dirs = Array("left", "right", "up", "down")
|
||||||
|
val dir = Random.nextInt(4)
|
||||||
|
|
||||||
|
dbg(Settings.toString)
|
||||||
|
dbg(CurrentState.board.toString())
|
||||||
|
|
||||||
|
dbg(dirs(dir))
|
||||||
|
|
||||||
|
dirs(dir)
|
||||||
|
}
|
||||||
|
}
|
76
bofa/src/main/scala/bot/Parser.scala
Normal file
76
bofa/src/main/scala/bot/Parser.scala
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import bot.{dbg,log}
|
||||||
|
|
||||||
|
object Parser {
|
||||||
|
|
||||||
|
def dispatch(input: String){
|
||||||
|
val parts: Seq[String] = input.split(" ")
|
||||||
|
|
||||||
|
parts(0) match {
|
||||||
|
case "settings" => settings(parts.tail)
|
||||||
|
case "update" => update(parts.tail)
|
||||||
|
case "action" => {
|
||||||
|
if(parts(1) equals "character") println("bixiette")
|
||||||
|
else println(Brain.move(parts(2).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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
bofa/src/main/scala/bot/State.scala
Normal file
123
bofa/src/main/scala/bot/State.scala
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import bot.dbg
|
||||||
|
|
||||||
|
class Field(val x: Int, val y: Int){
|
||||||
|
override def toString: String = s"Field { .x = $x, .y = $y }"
|
||||||
|
}
|
||||||
|
|
||||||
|
// static fields
|
||||||
|
case class EmptyCell (x0: Int, y0: Int) extends Field(x0,y0)
|
||||||
|
case class Wall (x0: Int, y0: Int) extends Field(x0,y0)
|
||||||
|
case class Gate (x0: Int, y0: Int, direction: String) extends Field(x0,y0)
|
||||||
|
case class BugSpawner (x0: Int, y0: Int, rounds: Option[Int]) extends Field(x0,y0)
|
||||||
|
// dynamic fields
|
||||||
|
case class PlayerPos (x0: Int, y0: Int, id: Int) extends Field(x0,y0)
|
||||||
|
case class BugPos (x0: Int, y0: Int, ai: Int) extends Field(x0,y0)
|
||||||
|
case class BombPos (x0: Int, y0: Int, rounds: Option[Int]) extends Field(x0,y0)
|
||||||
|
case class CodePos (x0: Int, y0: Int) extends Field(x0,y0)
|
||||||
|
|
||||||
|
|
||||||
|
class Player(id: Int){
|
||||||
|
val name: String = Settings.players(id)
|
||||||
|
var snippets: Int = 0
|
||||||
|
var bombs: Int = 0
|
||||||
|
|
||||||
|
override def toString = s"Player { .name = $name, .snippets = $snippets, .bombs = $bombs }"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Board {
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
def put(field: Field){
|
||||||
|
_board(field.x)(field.y) = field
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = {
|
||||||
|
val btp = _board.transpose
|
||||||
|
val buf = new StringBuilder
|
||||||
|
buf append "+" + "-" * (Settings.width*10+Settings.width*3-1) + "+" + "\n"
|
||||||
|
for(j <- 0 until Settings.height){
|
||||||
|
buf append s"""| ${btp(j) map (_.getClass.getSimpleName().padTo(10, ' ')) mkString (" | ")} |\n"""
|
||||||
|
}
|
||||||
|
buf append "+" + "-" * (Settings.width*10+Settings.width*3-1) + "+" + "\n"
|
||||||
|
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