Implement Bot prediction

Calculations of next step of every bot.
This commit is contained in:
Armin Friedl 2017-11-12 08:28:59 +01:00
parent c66e87791e
commit a1796c4198
58 changed files with 3693 additions and 45 deletions

View file

@ -7,6 +7,7 @@ import bot.util.Log.dbg
class Game {
var _round: Int = 0
var _timebank: Int = Settings.timebank
var lastBoard: Board = new Board(Settings.width, Settings.height)
var board: Board = new Board(Settings.width, Settings.height)
var bofa: Player = new Player(Settings.bofaId, Settings.bofaName)
var oppo: Player = new Player(Settings.oppoId, Settings.oppoName)
@ -37,7 +38,7 @@ class Game {
update match {
case round (rest) => this.round = rest.toInt
case board (rest) => this.board.parse(rest)
case board (rest) => this.lastBoard = this.board.clone(); this.board.parse(rest)
case snippets (name, rest) => playerBy(name).snippets = rest.toInt
case bombs (name, rest) => playerBy(name).bombs = rest.toInt
}

View file

@ -1,5 +1,8 @@
package bot
import bot.environment.topology.Direction
import bot.environment.topology.Stay
class Player(val id: Int, val name: String) {
var bombs: Int = 0
var snippets: Int = 0

View file

@ -1,10 +1,22 @@
package bot.cortex
import bot.environment.topology.Point
import bot.environment.Board
import bot.Game
import bot.environment.Board
import bot.environment.Board.Tokens
import bot.environment.Bot
import bot.environment.Bug
import bot.environment.Chase
import bot.environment.FarChase
import bot.environment.Lever
import bot.environment.Predict
import bot.environment.topology.Field
import bot.environment.topology.Grid
import bot.environment.topology.Point
import bot.util.Log.dbg
import bot.environment.Wall
import bot.environment.Spawner
import bot.environment.Gate
class BugAI extends Tactical {
def evaluate(game: Game): Tactic = {
@ -13,19 +25,69 @@ class BugAI extends Tactical {
val weightGrid = new Grid(board.width, board.height, 0: Int)
board.bugs foreach { weightGrid(_) put -1000 }
for ( bug <- board.bugs; point <- board.walkablePoints(bug.point) ) {
val field = weightGrid(point)
field put (field.content - 1000)
for ( bugField <- board.bugs ) {
weightGrid.put(bugField.point, -55)
bugField.content
.collect { case b@Bug(_) => b }
.foreach { bug =>
val goal = goalPoint(bug, bugField, game)
dbg(s"Predict Point [${bug.getClass.getSimpleName}]: ${goal.toString()}")
val walkPoint = bugWalkablePoint(bug, bugField.point, game) minBy { _ l2 goal }
dbg(s"Walk Point [${bug.getClass.getSimpleName}]: ${walkPoint.toString()}")
weightGrid.put(walkPoint, -1000) // don't collide
if (walkPoint == game.bofaPoint) weightGrid.put(bugField.point, -1000) // don't swap positions
}
}
board.walkableDirs(game.bofaPoint) foreach { dir =>
tactic = tactic + {dir -> weightGrid(board.walk(game.bofaPoint)(dir)).content}
}
board.walls foreach { w => weightGrid.put(w.point, -88) }
dbg(s"$weightGrid")
tactic
}
def goalPoint(bug: Bug, field: Field[Tokens], game: Game): Point = {
implicit def fieldToPoint (field: Field[_]): Point = field.point
implicit def gameToBoard (game: Game): Board = game.board
implicit def fieldToTokens (field: Field[Tokens]): Tokens = field.content
def predictPoint(newField: Field[Tokens]): Point = {
val botId = newField.content.collectFirst { case Bot(id) => id } get
val oldField = game.lastBoard.bots(botId) get
val point = newField.point - oldField.point
point + Point(point.x*3, point.y*3)
}
def nextBot(point: Point) = game.bots minBy { _ l2 point }
def bugPoint(goalPoint: Point): Point = game.walkablePoints(field) minBy {_ l2 goalPoint }
bug.ai match {
case Chase => nextBot(field)
case FarChase => game.bots maxBy { _ l2 field }
case Lever => val wantBugs = game.bugs filterNot { f => (f == field) && (f contains bug) }
val wantBug = if (wantBugs.isEmpty) field else wantBugs minBy { _ l2 field }
(wantBug.point - nextBot(field)) + (wantBug.point - nextBot(field))
case Predict => predictPoint(nextBot(field))
}
}
def bugWalkablePoint(bug: Bug, point: Point, game: Game): Set[Point] = {
val grid = game.board.asGrid
grid(point).content foreach { case Gate(d) => return Set(point-d); case _ => }
def oldField = game.lastBoard.bugs find { _.content.contains(bug) } getOrElse Field(Point(-1,-1), false)
grid.directions(point)
.filterNot { x => grid(point+x).content.contains(Wall()) }
.map { x => game.board.walk(point)(x) }
.filterNot { x => x == oldField.point }
}
}

View file

@ -25,8 +25,8 @@ class SnippetAI extends Tactical {
board.snippets foreach { snippet => distribute(snippet, weightGrid, board) }
preventFallBack(point, weightGrid, board)
weightGrid(game.bofaPoint) put -999
markWalls(weightGrid, board)
weightGrid.put(game.bofaPoint, 0) // from snippetai view-point staying is disadvised
markWalls(weightGrid, board) // just for visualization, not walkable anyway
dbg(weightGrid.toString())
board.walkableDirs(point) // get all possible directions starting in point
@ -42,14 +42,12 @@ class SnippetAI extends Tactical {
@tailrec
def runner(frontier: Set[Point], distance: Int): Unit = {
if (frontier.isEmpty) return
dbg(s"""Frontier[dist= $distance]: ${frontier.mkString(",")}""")
// process the current frontier and build the new one
val nextFrontier =
frontier filterNot { visited(_).get } flatMap { p =>
visited(p) put true
weightGrid(p).content += (1000 / math.pow(1.3, distance)).intValue
frontier filterNot { f => visited(f).content } flatMap { p =>
visited.put(p, true)
weightGrid.put(p, weightGrid(p).content + (1000 / math.pow(1.3, distance)).intValue)
board.walkablePoints(p)
}
@ -74,6 +72,6 @@ class SnippetAI extends Tactical {
}
private def markWalls(weightGrid: Grid[Int], board: Board) = {
board.walls foreach { wall => weightGrid(wall) put -88 }
board.walls foreach { wall => weightGrid.put(wall.point, -88) }
}
}

View file

@ -0,0 +1,17 @@
package bot.environment
sealed trait AIType
final case object Chase extends AIType
final case object Predict extends AIType
final case object Lever extends AIType
final case object FarChase extends AIType
object AIType {
def apply(ai: Int) = ai match {
case 0 => Chase
case 1 => Predict
case 2 => Lever
case 3 => FarChase
}
}

View file

@ -2,9 +2,9 @@ package bot.environment
import scala.collection.mutable
import bot.OptionInt
import bot.environment.Board.Tokens
import bot.environment.topology._
import scala.util.control.Exception.allCatch
object Board {
type Tokens = Set[Token]
@ -23,17 +23,17 @@ class Board(val width: Int, val height: Int) {
def snippets = _snippets.to[Set]
def bugs = _bugs.to[Set]
def bots(id: Int) = _bots.get(id)
def bots = _bots.values.to[Set]
def walls = _walls.to[Set]
def put(point: Point, tokens: Tokens) = {
val field = _grid(point)
field.content = tokens
_grid put (point, tokens)
tokens foreach {
case Snippet() => _snippets += field
case Bug(_) => _bugs += field
case Bot(id) => _bots += {id -> field}
case Wall() => _walls += field
case Snippet() => _snippets += _grid(point)
case Bug(_) => _bugs += _grid(point)
case Bot(id) => _bots += {id -> _grid(point)}
case Wall() => _walls += _grid(point)
case _ =>
}
}
@ -63,6 +63,19 @@ class Board(val width: Int, val height: Int) {
}
}
override def clone: Board = {
val that = new Board(width, height)
that._snippets.clear
that._bugs.clear
that._bots.clear
that._walls.clear
for(x <- 0 until width; y <- 0 until height)
that.put(Point(x,y), this.asGrid(x,y).content)
that
}
def parse(update: String): Unit = {
_snippets.clear
_bugs.clear
@ -78,6 +91,14 @@ class Board(val width: Int, val height: Int) {
private def unpack(things: String): Tokens = {
val tokens: mutable.Set[Token] = mutable.Set.empty
implicit class OptionInt(s: String) {
//TODO: change this to simple def returning Option[Int], just tested because its fancy
def toOptionInt: Option[Int] = s match {
case "" => None
case _ => allCatch.opt(s.toInt)
}
}
things
.split (';') // get single tokens
@ -87,12 +108,12 @@ class Board(val width: Int, val height: Int) {
case ("P", id) => Bot(id.toInt)
case ("G", "l") => Gate(Left)
case ("G", "r") => Gate(Right)
case ("E", ai) => Bug(ai.toInt)
case ("E", ai) => Bug(AIType(ai.toInt))
case ("C", _) => Snippet()
case ("S", time) => Spawner(time.toOptInt)
case ("B", time) => Bomb(time.toOptInt)
case ("S", time) => Spawner(time.toOptionInt)
case ("B", time) => Bomb(time.toOptionInt)
case _ => throw new MatchError } // map to token type
.map { tkn => tkn.asInstanceOf[Token] } // explicitely cast Array[Any] to Array[Token]
.toSet // return Array as Set[Token]
}
}
}

View file

@ -12,6 +12,6 @@ final case class Wall () extends Token
final case class Snippet () extends Token
final case class Gate (direction: Direction) extends Token
final case class Spawner (rounds: Option[Int]) extends Token
final case class Bug (ai: Int) extends Token
final case class Bug (ai: AIType) extends Token
final case class Bomb (rounds: Option[Int]) extends Token
final case class Bot (id: Int) extends Token

View file

@ -12,4 +12,5 @@ sealed trait Direction {
final case object Left extends { val label = "left"; val delta = (-1, 0) } with Direction
final case object Right extends { val label = "right"; val delta = (1, 0) } with Direction
final case object Up extends { val label = "up"; val delta = (0, -1) } with Direction
final case object Down extends { val label = "down"; val delta = (0, 1) } with Direction
final case object Down extends { val label = "down"; val delta = (0, 1) } with Direction
final case object Stay extends { val label = "pass"; val delta = (0, 0) } with Direction

View file

@ -1,13 +1,13 @@
package bot.environment.topology
class Field[T] (val point: Point, var content: T) {
class Field[T] (val point: Point, val content: T) {
def x: Int = point.x
def y: Int = point.y
def put(update: T) = content = update
def put(update: T) = new Field(point, update)
def get = content
def apply(f: T => T): Unit = content = f(content)
def apply(f: T => T): Unit = new Field(point, f(content))
}
object Field {
def apply[T] (point: Point, content: T) = new Field(point, content)

View file

@ -1,6 +1,7 @@
package bot.environment.topology
import scala.reflect.ClassTag
import bot.util.Log.dbg
/** The game world viewed as a grid
*
@ -9,13 +10,14 @@ import scala.reflect.ClassTag
class Grid[T: ClassTag] (width: Int, height: Int, default: => T) {
private val grid: Array[Array[Field[T]]] = Array.ofDim[Field[T]](width, height)
for (x <- 0 until width; y <- 0 until height)
grid(x)(y) = Field( Point(x,y), default )
for (x <- 0 until width; y <- 0 until height) put(Point(x,y), default)
def apply(x: Int, y: Int): Field[T] = grid(x)(y)
def apply(point: Point): Field[T] = apply(point.x, point.y)
def apply(f: Field[_]): Field[T] = apply(f.point)
def put(point: Point, update: T): Unit = grid(point.x)(point.y) = new Field(point, update)
def neighbors (point: Point): Set[Field[T]] = directions(point) map { dir => apply(point+dir) }
def directions (point: Point): Set[Direction] = {

View file

@ -6,8 +6,18 @@ class Point(val x: Int, val y: Int) {
def +(deltaX: Int, deltaY: Int): Point = new Point(x + deltaX, y + deltaY)
def +(delta: (Int, Int)): Point = this + (delta _1, delta _2)
def +(direction: Direction): Point = this + direction.delta
def +(point: Point): Point = this + point.asTuple
def -(deltaX: Int, deltaY: Int): Point = new Point(x - deltaX, y - deltaY)
def -(delta: (Int, Int)): Point = this - (delta _1, delta _2)
def -(direction: Direction): Point = this - direction.delta
def -(point: Point): Point = this - point.asTuple
// norms (l1 = manhattan, l2 = euclidian)
def l1 (to: Point): Double = (this.x - to.x).abs + (this.y - to.y).abs
def l2 (to: Point): Double = Math.sqrt( Math.pow(this.x-to.x, 2) + Math.pow(this.y-to.y, 2) )
override def toString: String = s"Point{.x=$x, .y=$y}"
def canEqual(a: Any) = a.isInstanceOf[Point]
override def equals(that: Any): Boolean =
that match {

View file

@ -1,10 +0,0 @@
import scala.util.control.Exception.allCatch
package object bot {
implicit class OptionInt(s: String) {
def toOptInt: Option[Int] = s match {
case "" => None
case _ => allCatch.opt(s.toInt)
}
}
}

View file

@ -0,0 +1,36 @@
# Created by .ignore support plugin (hsz.mobi)
### Gradle template
.gradle
build/
gradle/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### Java template
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.idea/
*.iml
!match-wrapper*
*resultfile.json

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 TheAIGames.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,47 @@
# Ms. Hack-man game engine
This repository contains the engine for the Booking.com Ms. Hack-man game for the Riddles.io platform.
## Setting up
This guide assumes the following software to be installed and globally
accessible:
- Gradle 2.14
- JVM 1.8.0_91
## Opening this project in IntelliJ IDEA
- Select 'Import Project'
- Browse to project directory root
- Select build.gradle
- Check settings:
- * Use local gradle distribution
- * Gradle home: /usr/share/gradle-2.14
- * Gradle JVM: 1.8
- * Project format: .idea (directory based)
*Note: for other IDEs, look at online documentation*
## Building the engine
Use Gradle to build a .jar of the engine. Go to Tasks -> build -> jar.
The .jar file can be found at `build/libs/`.
## Running
Running is handled by the MatchWrapper. This application handles all communication between
the engine and bots and stores the results of the match. To run, firstly edit the
`wrapper-commands.json` file. This should be pretty self-explanatory. Just change the command
fields to the right values to run the engine and the bots. In the example, the starterbot
is run twice, plus the command for the engine built in the previous step.
To run the MatchWrapper, use the following command (Linux):
```
java -jar match-wrapper.jar "$(cat wrapper-commands.json)"
```
Have a look at the MatchWrapper repo for more details about it:
[https://github.com/riddlesio/match-wrapper](https://github.com/riddlesio/match-wrapper)
*Note: if running on other systems, find how to put the content of wrapper-commands.json as
argument when running the match-wrapper.jar*

View file

@ -0,0 +1,65 @@
group 'io.riddles'
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'application'
apply plugin: 'com.jfrog.artifactory'
version = '1.0.5'
mainClassName = 'io.riddles.hackman2.HackMan2'
sourceCompatibility = 1.8
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.4.0'
}
}
artifactory {
resolve {
contextUrl = 'http://artifactory.dev.riddles.io/artifactory'
repoKey = 'libs-release-local'
username = 'anonymous'
}
}
sourceSets {
main {
java {
srcDir 'src/java'
}
}
test {
groovy {
srcDir 'test/groovy'
}
}
}
jar {
manifest {
attributes 'Implementation-Title': 'Hack-man 2 Game Engine',
'Implementation-Version': version,
'Main-Class': mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'io.riddles', name: 'javainterface', version: '1.0.7'
compile group: 'org.json', name: 'json', version: '20160212'
testCompile 'org.codehaus.groovy:groovy-all:2.4.1'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
}

172
hack-man-2-engine-development/gradlew vendored Executable file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

Binary file not shown.

View file

@ -0,0 +1,5 @@
#!/bin/bash
BASEDIR=`pwd`
java -jar $BASEDIR/match-wrapper-*.jar "$(cat wrapper-commands.json)"
echo "${?}"

View file

@ -0,0 +1,2 @@
rootProject.name = 'hackman2-engine-java'

View file

@ -0,0 +1,44 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2;
import io.riddles.hackman2.engine.HackMan2Engine;
import io.riddles.hackman2.game.state.HackMan2State;
import io.riddles.javainterface.game.player.PlayerProvider;
import io.riddles.javainterface.io.IOHandler;
/**
* io.riddles.hackman2.HackMan2 - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2 {
public static void main(String[] args) throws Exception {
HackMan2Engine engine = new HackMan2Engine(new PlayerProvider<>(), new IOHandler());
HackMan2State firstState = engine.willRun();
HackMan2State finalState = engine.run(firstState);
engine.didRun(firstState, finalState);
}
}

View file

@ -0,0 +1,239 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.engine;
import java.awt.*;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.UUID;
import io.riddles.hackman2.game.HackMan2Serializer;
import io.riddles.hackman2.game.board.Gate;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.enemy.EnemySpawnPoint;
import io.riddles.hackman2.game.move.ActionType;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.player.HackMan2Player;
import io.riddles.hackman2.game.player.CharacterType;
import io.riddles.hackman2.game.processor.HackMan2Processor;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
import io.riddles.javainterface.configuration.Configuration;
import io.riddles.javainterface.engine.AbstractEngine;
import io.riddles.javainterface.engine.GameLoopInterface;
import io.riddles.javainterface.engine.SimpleGameLoop;
import io.riddles.javainterface.exception.TerminalException;
import io.riddles.javainterface.game.player.PlayerProvider;
import io.riddles.javainterface.io.IOInterface;
/**
* io.riddles.hackman2.engine.HackMan2Engine - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Engine extends AbstractEngine<HackMan2Processor, HackMan2Player, HackMan2State> {
public static SecureRandom RANDOM;
public HackMan2Engine(PlayerProvider<HackMan2Player> playerProvider, IOInterface ioHandler) throws TerminalException {
super(playerProvider, ioHandler);
}
@Override
protected Configuration getDefaultConfiguration() {
Configuration configuration = new Configuration();
configuration.put("maxRounds", 250); // 250
configuration.put("playerSnippetCount", 0);
configuration.put("mapSnippetCount", 2);
configuration.put("snippetSpawnRate", 8);
configuration.put("snippetSpawnCount", 1);
configuration.put("initialEnemyCount", 0);
configuration.put("enemySpawnDelay", 0);
configuration.put("enemySpawnRate", 4); // 4
configuration.put("enemySnippetLoss", 4);
configuration.put("enemySpawnTime", 3);
configuration.put("mapBombCount", 0); // 0
configuration.put("bombSpawnDelay", 2); // 2
configuration.put("bombSpawnRate", 5); // 5
configuration.put("bombSnippetLoss", 4);
configuration.put("bombMinTicks", 2);
configuration.put("bombMaxTicks", 5);
configuration.put("fieldWidth", 19);
configuration.put("fieldHeight", 15);
configuration.put("fieldLayout", getDefaultFieldLayout());
configuration.put("seed", UUID.randomUUID().toString());
return configuration;
}
@Override
protected HackMan2Processor createProcessor() {
return new HackMan2Processor(this.playerProvider);
}
@Override
protected GameLoopInterface createGameLoop() {
return new SimpleGameLoop();
}
@Override
protected HackMan2Player createPlayer(int id) {
return new HackMan2Player(id);
}
@Override
protected void sendSettingsToPlayer(HackMan2Player player) {
player.sendSetting("your_botid", player.getId());
player.sendSetting("field_width", configuration.getInt("fieldWidth"));
player.sendSetting("field_height", configuration.getInt("fieldHeight"));
player.sendSetting("max_rounds", configuration.getInt("maxRounds"));
}
@Override
protected HackMan2State getInitialState() {
setRandomSeed();
// Ask which character the bot wants to play as
requestPlayerCharacters();
int width = configuration.getInt("fieldWidth");
int height = configuration.getInt("fieldHeight");
String layout = getDefaultFieldLayout();
ArrayList<EnemySpawnPoint> enemySpawnPoints = getEnemySpawnPoints();
ArrayList<Gate> gates = getGates();
// Create board
HackMan2Board board = new HackMan2Board(width, height, layout, enemySpawnPoints, gates);
// Create player states
ArrayList<HackMan2PlayerState> playerStates = getInitialPlayerStates();
HackMan2State initialState = new HackMan2State(playerStates, board);
// Spawn initial items
board.spawnInitialObjects(initialState);
return initialState;
}
protected ArrayList<HackMan2PlayerState> getInitialPlayerStates() {
ArrayList<HackMan2PlayerState> playerStates = new ArrayList<>();
for (HackMan2Player player : this.playerProvider.getPlayers()) {
int id = player.getId();
Point startingCoordinate = getStartingCoordinates(id);
HackMan2PlayerState playerState = new HackMan2PlayerState(id, startingCoordinate);
playerStates.add(playerState);
}
return playerStates;
}
@Override
protected String getPlayedGame(HackMan2State initialState) {
HackMan2Serializer serializer = new HackMan2Serializer();
return serializer.traverseToString(this.processor, initialState);
}
private Point getStartingCoordinates(int id) {
switch (id) {
case 0:
return new Point(4, 7);
case 1:
return new Point(14, 7);
}
return null;
}
protected String getDefaultFieldLayout() {
return ".,.,.,x,.,.,.,.,.,.,.,.,.,.,.,x,.,.,.," +
".,x,.,x,.,x,x,x,x,.,x,x,x,x,.,x,.,x,.," +
".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," +
".,x,x,x,.,x,.,x,x,x,x,x,.,x,.,x,x,x,.," +
".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," +
".,.,.,x,.,x,.,x,x,.,x,x,.,x,.,x,.,.,.," +
"x,.,x,x,.,.,.,x,x,.,x,x,.,.,.,x,x,.,x," +
".,.,x,x,.,x,x,x,x,.,x,x,x,x,.,x,x,.,.," +
"x,.,x,x,.,.,.,.,.,.,.,.,.,.,.,x,x,.,x," +
".,.,.,x,.,x,x,x,x,x,x,x,x,x,.,x,.,.,.," +
".,x,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,x,.," +
".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," +
".,x,.,x,x,.,x,x,x,x,x,x,x,.,x,x,.,x,.," +
".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," +
".,.,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,.,.";
}
protected ArrayList<EnemySpawnPoint> getEnemySpawnPoints() {
ArrayList<EnemySpawnPoint> spawnPoints = new ArrayList<>();
spawnPoints.add(new EnemySpawnPoint(new Point(0, 0), 0));
spawnPoints.add(new EnemySpawnPoint(new Point(18, 0), 1));
spawnPoints.add(new EnemySpawnPoint(new Point(18, 14), 2));
spawnPoints.add(new EnemySpawnPoint(new Point(0, 14), 3));
return spawnPoints;
}
protected ArrayList<Gate> getGates() {
ArrayList<Gate> gates = new ArrayList<>();
Gate gate1 = new Gate(new Point(0, 7), MoveType.LEFT);
Gate gate2 = new Gate(new Point(18, 7), MoveType.RIGHT);
gate1.setLinkedGate(gate2);
gate2.setLinkedGate(gate1);
gates.add(gate1);
gates.add(gate2);
return gates;
}
private void requestPlayerCharacters() {
for (HackMan2Player player : this.playerProvider.getPlayers()) {
String response = player.requestMove(ActionType.CHARACTER);
CharacterType character = CharacterType.fromString(response);
if (character == null) {
character = CharacterType.getRandomCharacter();
}
player.setCharacterType(character);
}
}
protected void setRandomSeed() {
try {
RANDOM = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException ex) {
LOGGER.severe("Not able to use SHA1PRNG, using default algorithm");
RANDOM = new SecureRandom();
}
String seed = configuration.getString("seed");
LOGGER.info("RANDOM SEED IS: " + seed);
RANDOM.setSeed(seed.getBytes());
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game;
import java.awt.Point;
/**
* io.riddles.hackman2.game.board.Item - Created on 12-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public abstract class HackMan2Object {
protected Point coordinate;
public HackMan2Object(Point coordinate) {
this.coordinate = coordinate;
}
public HackMan2Object(HackMan2Object object) {
this.coordinate = new Point(object.coordinate);
}
public abstract String toString();
public Point getCoordinate() {
return this.coordinate;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game;
import org.json.JSONObject;
import io.riddles.javainterface.serialize.Serializer;
/**
* io.riddles.hackman2.game.HackMan2ObjectSerializer - Created on 15-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2ObjectSerializer implements Serializer<HackMan2Object> {
@Override
public String traverseToString(HackMan2Object object) {
return visitObject(object).toString();
}
@Override
public JSONObject traverseToJson(HackMan2Object object) {
return visitObject(object);
}
private JSONObject visitObject(HackMan2Object object) {
JSONObject objectObj = new JSONObject();
objectObj.put("x", object.getCoordinate().x);
objectObj.put("y", object.getCoordinate().y);
return objectObj;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game;
import org.json.JSONArray;
import org.json.JSONObject;
import io.riddles.hackman2.game.player.HackMan2Player;
import io.riddles.hackman2.game.processor.HackMan2Processor;
import io.riddles.hackman2.game.state.HackMan2State;
import io.riddles.hackman2.game.state.HackMan2StateSerializer;
import io.riddles.javainterface.game.AbstractGameSerializer;
/**
* io.riddles.hackman2.game.HackMan2Serializer - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Serializer extends AbstractGameSerializer<HackMan2Processor, HackMan2State> {
@Override
public String traverseToString(HackMan2Processor processor, HackMan2State initialState) {
HackMan2StateSerializer stateSerializer = new HackMan2StateSerializer();
JSONObject game = new JSONObject();
game = addDefaultJSON(initialState, game, processor);
JSONArray characters = new JSONArray();
for (HackMan2Player player : processor.getPlayerProvider().getPlayers()) {
JSONObject characterObj = new JSONObject();
characterObj.put("id", player.getId());
characterObj.put("type", player.getCharacterType());
characters.put(characterObj);
}
JSONArray states = new JSONArray();
states.put(stateSerializer.traverseToJson(initialState));
HackMan2State state = initialState;
while (state.hasNextState()) {
state = (HackMan2State) state.getNextState();
states.put(stateSerializer.traverseToJson(state));
}
game.put("characters", characters);
game.put("states", states);
return game.toString();
}
}

View file

@ -0,0 +1,77 @@
package io.riddles.hackman2.game.board;
import java.awt.*;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.board.Board - Created on 9-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public abstract class Board {
protected final String EMTPY_FIELD = ".";
protected final String BLOCKED_FIELD = "x";
protected int width;
protected int height;
protected String layout;
protected String[][] fields;
Board(int width, int height, String layout) {
this.width = width;
this.height = height;
this.layout = layout;
this.fields = new String[width][height];
setFieldsFromString(layout);
}
public boolean isDirectionValid(Point coordinate, MoveType direction) {
if (direction == null) {
return false;
}
Point newCoordinate = direction.getCoordinateAfterMove(coordinate);
return isCoordinateValid(newCoordinate);
}
public boolean isCoordinateValid(Point coordinate) {
int x = coordinate.x;
int y = coordinate.y;
return x >= 0 && y >= 0 && x < this.width && y < this.height &&
!this.fields[x][y].equals(BLOCKED_FIELD);
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
public String getLayout() {
return this.layout;
}
private void setFieldsFromString(String input) {
String[] split = input.split(",");
int x = 0;
int y = 0;
for (String fieldString : split) {
this.fields[x][y] = fieldString;
if (++x == this.width) {
x = 0;
y++;
}
}
}
}

View file

@ -0,0 +1,41 @@
package io.riddles.hackman2.game.board;
import java.awt.*;
import io.riddles.hackman2.game.HackMan2Object;
import io.riddles.hackman2.game.move.MoveType;
/**
* io.riddles.hackman2.game.board.Gate - Created on 18-7-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class Gate extends HackMan2Object {
private MoveType entry;
private Gate linkedGate;
public Gate(Point coordinate, MoveType entry) {
super(coordinate);
this.entry = entry;
}
@Override
public String toString() {
return String.format("G%s", this.entry.toString().charAt(0));
}
public MoveType getEntry() {
return this.entry;
}
public Gate getLinkedGate() {
return this.linkedGate;
}
public void setLinkedGate(Gate linkedGate) {
this.linkedGate = linkedGate;
}
}

View file

@ -0,0 +1,508 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.board;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import io.riddles.hackman2.engine.HackMan2Engine;
import io.riddles.hackman2.game.HackMan2Object;
import io.riddles.hackman2.game.enemy.Enemy;
import io.riddles.hackman2.game.enemy.EnemyDeath;
import io.riddles.hackman2.game.enemy.EnemySpawnPoint;
import io.riddles.hackman2.game.item.Bomb;
import io.riddles.hackman2.game.item.Snippet;
import io.riddles.hackman2.game.move.HackMan2Move;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
import io.riddles.javainterface.configuration.Configuration;
import io.riddles.javainterface.exception.InvalidMoveException;
/**
* io.riddles.hackman2.game.board.HackMan2Board - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Board extends Board {
private ArrayList<Enemy> enemies;
private HashMap<String, Bomb> bombs;
private HashMap<String, Snippet> snippets;
private HashMap<String, Gate> gates;
private ArrayList<EnemySpawnPoint> enemySpawnPoints;
private int spawnedEnemies;
private int spawnedBombs;
public HackMan2Board(int width, int height, String layout,
ArrayList<EnemySpawnPoint> enemySpawnPoints, ArrayList<Gate> gates) {
super(width, height, layout);
this.enemySpawnPoints = enemySpawnPoints;
this.enemies = new ArrayList<>();
this.bombs = new HashMap<>();
this.snippets = new HashMap<>();
this.gates = new HashMap<>();
this.spawnedEnemies = 0;
this.spawnedBombs = 0;
gates.forEach(gate -> this.gates.put(gate.getCoordinate().toString(), gate));
}
public HackMan2Board(HackMan2Board board) {
super(board.getWidth(), board.getHeight(), board.getLayout());
this.enemySpawnPoints = board.enemySpawnPoints;
this.gates = board.gates;
this.spawnedEnemies = board.spawnedEnemies;
this.spawnedBombs = board.spawnedBombs;
this.enemies = board.enemies.stream()
.map(Enemy::new)
.collect(Collectors.toCollection(ArrayList::new));
this.bombs = board.bombs.entrySet().stream()
.collect(
HashMap::new,
(m, e) -> m.put(e.getKey(), new Bomb(e.getValue())),
Map::putAll
);
this.snippets = board.snippets.entrySet().stream()
.collect(
HashMap::new,
(m, e) -> m.put(e.getKey(), new Snippet(e.getValue())),
Map::putAll
);
}
public String toString(HackMan2State state) {
String[][] outFields = new String[this.width][this.height];
// Create a new 2d array to store the actual contents of each field
for (int y = 0; y < this.height; y++) {
for (int x = 0; x < this.width; x++) {
if (this.fields[x][y].equals(BLOCKED_FIELD)) {
outFields[x][y] = BLOCKED_FIELD;
} else {
outFields[x][y] = EMTPY_FIELD;
}
}
}
// Add the string representations of each object on the board
for (HackMan2PlayerState playerState : state.getPlayerStates()) {
Point coordinate = playerState.getCoordinate();
outFields[coordinate.x][coordinate.y] += playerState.toString() + ";";
}
this.enemySpawnPoints.forEach(spawnPoint -> addObjectToOutFields(outFields, spawnPoint));
this.gates.forEach((key, gate) -> addObjectToOutFields(outFields, gate));
getAliveEnemies().forEach(enemy -> addObjectToOutFields(outFields, enemy));
this.bombs.forEach((key, bomb) -> addObjectToOutFields(outFields, bomb));
this.snippets.forEach((key, snippet) -> addObjectToOutFields(outFields, snippet));
// Create the string from the 2d array
StringBuilder output = new StringBuilder();
for (int y = 0; y < this.height; y++) {
for (int x = 0; x < this.width; x++) {
String value = outFields[x][y];
if (value.length() > 1) {
value = value.substring(1, value.length() - 1);
}
output.append(value).append(",");
}
}
output.setLength(output.length() - 1);
return output.toString();
}
public void spawnInitialObjects(HackMan2State state) {
for (int i = 0; i < HackMan2Engine.configuration.getInt("mapSnippetCount"); i++) {
spawnSnippet(state);
}
for (int i = 0; i < HackMan2Engine.configuration.getInt("mapBombCount"); i++) {
spawnBomb(state);
}
}
public void validateMove(HackMan2PlayerState playerState, HackMan2Move move) {
if (move.isInvalid()) return;
MoveType moveType = move.getMoveType();
Point oldCoordinate = playerState.getCoordinate();
Point newCoordinate = getCoordinateAfterMove(moveType, oldCoordinate);
if (!isCoordinateValid(newCoordinate)) {
move.setException(new InvalidMoveException("Can't move this direction"));
}
}
public Point getCoordinateAfterMove(MoveType moveType, Point coordinate) {
Gate gate = this.gates.get(coordinate.toString());
// Move through gate
if (gate != null && gate.getEntry() == moveType) {
return gate.getLinkedGate().getCoordinate();
}
// Normal move
return moveType.getCoordinateAfterMove(coordinate);
}
public void cleanUpEnemies() {
this.enemies = getAliveEnemies();
}
public void moveEnemies(HackMan2State state) {
this.enemies.forEach(enemy -> enemy.getNewCoordinate(state));
this.enemies.forEach(Enemy::performMovement);
}
public void performPickups(HackMan2State state) {
pickupSnippets(state);
pickupBombs(state);
}
public void performCollisions(HackMan2State previousState, HackMan2State state) {
swapCollideWithEnemies(previousState, state);
detonateBombs(state);
positionCollideWithEnemies(state);
}
public void spawnEnemies() {
for (EnemySpawnPoint spawnPoint : this.enemySpawnPoints) {
spawnPoint.reduceSpawnTime();
Enemy newEnemy = spawnPoint.spawnEnemy(this.spawnedEnemies);
if (newEnemy == null) continue;
this.enemies.add(newEnemy);
this.spawnedEnemies++;
}
}
public void spawnObjects(HackMan2State state) {
startEnemySpawn(state);
spawnSnippets(state);
spawnBombs(state);
}
public void dropBomb(Point coordinate, int ticks) {
this.bombs.put(coordinate.toString(), new Bomb(coordinate, ticks));
}
public ArrayList<Enemy> getEnemies() {
return this.enemies;
}
public HashMap<String, Bomb> getBombs() {
return this.bombs;
}
public HashMap<String, Snippet> getSnippets() {
return this.snippets;
}
private void addObjectToOutFields(String[][] outFields, HackMan2Object object) {
Point coordinate = object.getCoordinate();
outFields[coordinate.x][coordinate.y] += object.toString() + ";";
}
private void pickupSnippets(HackMan2State state) {
ArrayList<HackMan2PlayerState> playersOnSnippet = getPlayersOnItem(state, this.snippets);
for (HackMan2PlayerState playerState : playersOnSnippet) {
playerState.updateSnippets(1);
state.addSnippetCollected();
this.snippets.remove(playerState.getCoordinate().toString());
}
}
private void pickupBombs(HackMan2State state) {
HashMap<String, Bomb> collectableBombs = this.bombs.entrySet().stream()
.filter(e -> e.getValue().getTicks() == null)
.collect(HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll);
ArrayList<HackMan2PlayerState> playersOnBomb = getPlayersOnItem(state, collectableBombs);
for (HackMan2PlayerState playerState : playersOnBomb) {
playerState.updateBombs(1);
this.bombs.remove(playerState.getCoordinate().toString());
}
}
private void detonateBombs(HackMan2State state) {
this.bombs.forEach((key, bomb) -> bomb.tick());
ArrayList<String> explodingCoordinates = explodeBombs();
blastEnemies(explodingCoordinates);
blastPlayers(state, explodingCoordinates);
}
private ArrayList<HackMan2PlayerState> getPlayersOnItem(HackMan2State state, Map itemMap) {
ArrayList<HackMan2PlayerState> playersNotOnSameCoord = getPlayersNotOnSameCoordinate(state);
// Return list of playerStates that have same coord as some item map
return playersNotOnSameCoord.stream()
.filter(ps -> itemMap.containsKey(ps.getCoordinate().toString()))
.collect(Collectors.toCollection(ArrayList::new));
}
private ArrayList<HackMan2PlayerState> getPlayersNotOnSameCoordinate(HackMan2State state) {
ArrayList<HackMan2PlayerState> playerStates = new ArrayList<>(state.getPlayerStates());
// Shuffle to get random player on same coordinate
Collections.shuffle(playerStates, HackMan2Engine.RANDOM);
// Remove players on same coordinate
return new ArrayList<>(playerStates.stream()
.<Map<String, HackMan2PlayerState>> collect(
HashMap::new,
(m, ps) -> m.put(ps.getCoordinate().toString(), ps),
Map::putAll
)
.values());
}
private void swapCollideWithEnemies(HackMan2State previousState, HackMan2State state) {
ArrayList<Enemy> aliveEnemies = getAliveEnemies();
for (HackMan2PlayerState playerState : state.getPlayerStates()) {
Point coord = playerState.getCoordinate();
Point previousCoord = previousState.getPlayerStateById(
playerState.getPlayerId()).getCoordinate();
for (Enemy enemy : aliveEnemies) {
Enemy previousEnemy = previousState.getBoard().getEnemyById(enemy.getId());
if (previousEnemy == null) continue;
Point enemyCoord = enemy.getCoordinate();
Point previousEnemyCoord = previousEnemy.getCoordinate();
if (coord.equals(previousEnemyCoord) && enemyCoord.equals(previousCoord)) {
hitPlayerWithEnemy(playerState);
enemy.kill(EnemyDeath.ATTACK);
}
}
}
}
private void positionCollideWithEnemies(HackMan2State state) {
ArrayList<Enemy> aliveEnemies = state.getBoard().getAliveEnemies();
state.getPlayerStates().forEach(playerState ->
aliveEnemies.stream()
.filter(enemy -> enemy.getCoordinate().equals(playerState.getCoordinate()))
.forEach(enemy -> {
hitPlayerWithEnemy(playerState);
enemy.kill(EnemyDeath.ATTACK);
}));
}
private ArrayList<Enemy> getAliveEnemies() {
return this.enemies.stream()
.filter(Enemy::isAlive)
.collect(Collectors.toCollection(ArrayList::new));
}
protected ArrayList<String> explodeBombs() {
ArrayList<String> explodingCoordinates = new ArrayList<>();
ArrayList<Bomb> explodedBombs = new ArrayList<>();
this.bombs.entrySet().stream()
.filter(e -> e.getValue().getTicks() != null && e.getValue().getTicks() == 0)
.forEach(e -> explodeBomb(e.getValue(), explodingCoordinates, explodedBombs));
// Remove all exploded bombs at the end
for (Bomb exploded : explodedBombs) {
this.bombs.remove(exploded.getCoordinate().toString());
}
return explodingCoordinates;
}
private void explodeBomb(Bomb bomb, ArrayList<String> explodingCoordinates, ArrayList<Bomb> explodingBombs) {
Point coordinate = bomb.getCoordinate();
explodingBombs.add(bomb);
addExplodingCoordinate(coordinate, explodingCoordinates);
explodeInDirection(coordinate, new Point(0, -1), explodingCoordinates, explodingBombs);
explodeInDirection(coordinate, new Point(1, 0), explodingCoordinates, explodingBombs);
explodeInDirection(coordinate, new Point(0, 1), explodingCoordinates, explodingBombs);
explodeInDirection(coordinate, new Point(-1, 0), explodingCoordinates, explodingBombs);
}
private void explodeInDirection(Point coordinate, Point direction,
ArrayList<String> explodingCoordinates, ArrayList<Bomb> explodingBombs) {
Point newCoordinate = new Point(coordinate.x + direction.x, coordinate.y + direction.y);
if (!isCoordinateValid(newCoordinate)) return;
addExplodingCoordinate(newCoordinate, explodingCoordinates);
explodeInDirection(newCoordinate, direction, explodingCoordinates, explodingBombs); // recursive call
Bomb nextBomb = this.bombs.get(newCoordinate.toString());
if (nextBomb == null || explodingBombs.contains(nextBomb) || nextBomb.getTicks() == null) {
return;
}
// explode bombs that are hit in the blast
explodeBomb(nextBomb, explodingCoordinates, explodingBombs);
}
private void blastEnemies(ArrayList<String> explodingCoordinates) {
this.enemies.stream()
.filter(enemy -> explodingCoordinates.contains(enemy.getCoordinate().toString()))
.forEach(enemy -> enemy.kill(EnemyDeath.BOMBED));
}
private void blastPlayers(HackMan2State state, ArrayList<String> explodingCoordinates) {
state.getPlayerStates().stream()
.filter(ps -> explodingCoordinates.contains(ps.getCoordinate().toString()))
.forEach(ps -> hitPlayerWithBomb(state, ps));
}
private void hitPlayerWithEnemy(HackMan2PlayerState playerState) {
int snippetLoss = HackMan2Engine.configuration.getInt("enemySnippetLoss");
playerState.updateSnippets(-snippetLoss);
}
private void hitPlayerWithBomb(HackMan2State state, HackMan2PlayerState playerState) {
int snippetLoss = HackMan2Engine.configuration.getInt("bombSnippetLoss");
int playerSnippets = playerState.getSnippets();
int spawnCount = playerSnippets < snippetLoss ? playerSnippets : snippetLoss;
playerState.updateSnippets(-snippetLoss);
for (int n = 0; n < spawnCount; n++) {
spawnSnippet(state);
}
}
private void addExplodingCoordinate(Point coordinate, ArrayList<String> explodingCoordinates) {
String key = coordinate.toString();
if (explodingCoordinates.contains(key)) return;
explodingCoordinates.add(key);
}
private Point getRandomSpawnPoint(HackMan2State state) {
ArrayList<Point> spawnPoints = getItemSpawnPoints(state);
int randomIndex = HackMan2Engine.RANDOM.nextInt(spawnPoints.size());
return spawnPoints.get(randomIndex);
}
private ArrayList<Point> getItemSpawnPoints(HackMan2State state) {
ArrayList<Point> spawnPoints = new ArrayList<>();
for (int y = 0; y < this.height; y++) {
for (int x = 0; x < this.width; x++) {
Point coordinate = new Point(x, y);
if (!isCoordinateValid(coordinate)) continue;
// Items can't spawn closer than 5 to each player
boolean distant = state.getPlayerStates().stream()
.allMatch(p -> p.getCoordinate().distance(coordinate) > 5);
boolean onOtherItem = this.snippets.containsKey(coordinate.toString()) ||
this.bombs.containsKey(coordinate.toString());
if (distant && !onOtherItem) {
spawnPoints.add(coordinate);
}
}
}
return spawnPoints;
}
private Enemy getEnemyById(int id) {
return this.enemies.stream()
.filter(enemy -> enemy.getId() == id)
.findFirst()
.orElse(null);
}
// Based on the amount of snippets
private void startEnemySpawn(HackMan2State state) {
Configuration config = HackMan2Engine.configuration;
int snippetsCollected = state.getSnippetsCollected();
int spawningSpawnPoints = (int) this.enemySpawnPoints.stream()
.filter(sp -> sp.spawnTime != null)
.count();
int totalEnemies = spawningSpawnPoints + this.spawnedEnemies;
int enemiesToSpawn = ((snippetsCollected - config.getInt("enemySpawnDelay")) /
config.getInt("enemySpawnRate")) - totalEnemies;
for (int id = this.spawnedEnemies; id < this.spawnedEnemies + enemiesToSpawn; id++) {
int spawnType = totalEnemies % 4;
this.enemySpawnPoints.get(spawnType).setSpawnTime(config.getInt("enemySpawnTime"));
}
}
// Based on rounds
private void spawnSnippets(HackMan2State state) {
Configuration config = HackMan2Engine.configuration;
if (state.getRoundNumber() % config.getInt("snippetSpawnRate") == 0) {
for (int n = 0; n < config.getInt("snippetSpawnCount"); n++) {
spawnSnippet(state);
}
}
}
// Based on amount of snippets
private void spawnBombs(HackMan2State state) {
Configuration config = HackMan2Engine.configuration;
int snippetsCollected = state.getSnippetsCollected();
int bombsToSpawn = ((snippetsCollected - config.getInt("bombSpawnDelay")) /
config.getInt("bombSpawnRate")) - this.spawnedBombs;
for (int n = 0; n < bombsToSpawn; n++) {
spawnBomb(state);
}
}
private void spawnSnippet(HackMan2State state) {
Point coordinate = getRandomSpawnPoint(state);
this.snippets.put(coordinate.toString(), new Snippet(coordinate));
}
private void spawnBomb(HackMan2State state) {
Point coordinate = getRandomSpawnPoint(state);
this.bombs.put(coordinate.toString(), new Bomb(coordinate, null));
this.spawnedBombs++;
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;
import io.riddles.hackman2.engine.HackMan2Engine;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.AbstractEnemyAI - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public abstract class AbstractEnemyAI {
public abstract Point transform(Enemy enemy, HackMan2State state);
ArrayList<MoveType> getAvailableDirections(Enemy enemy, HackMan2Board board) {
MoveType direction = enemy.getDirection() != null ? enemy.getDirection() : MoveType.PASS;
return MoveType.getMovingMoveTypes().stream()
.filter(moveType -> !moveType.equals(direction.getOppositeMoveType()) &&
board.isDirectionValid(enemy.getCoordinate(), moveType))
.collect(Collectors.toCollection(ArrayList::new));
}
/**
* If there are any transformations the enemy MUST make, that value is returned.
*/
Point mandatoryTransform(Enemy enemy, ArrayList<MoveType> availableDirections) {
Point coordinate = enemy.getCoordinate();
switch (availableDirections.size()) {
// No directions available, turn around
case 0:
MoveType oppositeDirection = enemy.getDirection().getOppositeMoveType();
return oppositeDirection.getCoordinateAfterMove(coordinate);
// Only one direction available, go that way
case 1:
return availableDirections.get(0).getCoordinateAfterMove(coordinate);
}
return null;
}
/**
* Returns next point (1 step) the enemy must move to if it wants to get closer
* to given goal.
*/
Point transformToGoal(Point coordinate, Point goal, ArrayList<MoveType> availableDirections) {
// Map to the coordinates after moving in each available direction
ArrayList<Point> movedCoordinates = availableDirections.stream()
.map(direction -> direction.getCoordinateAfterMove(coordinate))
.collect(Collectors.toCollection(ArrayList::new));
// Shuffle so min returns a random value on same distances
Collections.shuffle(movedCoordinates, HackMan2Engine.RANDOM);
// Return coordinate with smallest euclidean distance
return movedCoordinates.stream()
.min(Comparator.comparingDouble(goal::distance))
.orElseThrow(() -> new RuntimeException("Failed to find shortest distance"));
}
/**
* Returns the player states sorted from closest to farthest to enemy
*/
ArrayList<HackMan2PlayerState> getSortedPlayers(Point coordinate, HackMan2State state) {
ArrayList<HackMan2PlayerState> playerStates = new ArrayList<>(state.getPlayerStates());
// Shuffle to get random player state on same distance values
Collections.shuffle(playerStates, HackMan2Engine.RANDOM);
// Sort from closest to farthest away from enemy coordinate
playerStates.sort(
Comparator.comparingDouble(ps -> coordinate.distance(ps.getCoordinate())));
return playerStates;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.Point;
import java.util.ArrayList;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.ChaseAI - Created on 8-6-17
*
* This enemy AI will always move to the current location
* of the closest player.
*
* @author Jim van Eeden - jim@riddles.io
*/
public class ChaseAI extends AbstractEnemyAI {
@Override
public Point transform(Enemy enemy, HackMan2State state) {
HackMan2Board board = state.getBoard();
Point coordinate = enemy.getCoordinate();
ArrayList<MoveType> availableDirections = getAvailableDirections(enemy, board);
Point mandatoryTransform = mandatoryTransform(enemy, availableDirections);
if (mandatoryTransform != null) { // Mandatory directions are always taken
return mandatoryTransform;
}
HackMan2PlayerState closestPlayer = getSortedPlayers(coordinate, state).get(0);
return transformToGoal(coordinate, closestPlayer.getCoordinate(), availableDirections);
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import io.riddles.hackman2.game.HackMan2Object;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.Enemy - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class Enemy extends HackMan2Object {
private int id;
private int type;
private boolean isAlive;
private EnemyDeath deathType;
private MoveType direction;
private AbstractEnemyAI enemyAI;
private Point newCoordinate; // Stores new coordinate after moving
public Enemy(int id, Point coordinate, int type) {
super(coordinate);
this.id = id;
this.type = type;
this.isAlive = true;
switch (type) {
case 0:
this.enemyAI = new ChaseAI();
break;
case 1:
this.enemyAI = new PredictAI();
break;
case 2:
this.enemyAI = new LeverAI();
break;
case 3:
this.enemyAI = new FarChaseAI();
break;
}
}
public Enemy(Enemy enemy) {
super(enemy);
this.id = enemy.id;
this.type = enemy.type;
this.direction = enemy.direction;
this.enemyAI = enemy.enemyAI;
this.isAlive = enemy.isAlive;
this.deathType = enemy.deathType;
}
public String toString() {
return String.format("E%d", this.type);
}
public void getNewCoordinate(HackMan2State state) {
this.newCoordinate = this.enemyAI.transform(this, state);
}
public void performMovement() {
this.direction = getNewDirection(this.coordinate, this.newCoordinate);
this.coordinate = this.newCoordinate;
this.newCoordinate = null;
}
public void kill(EnemyDeath deathType) {
this.isAlive = false;
this.deathType = deathType;
}
public int getId() {
return this.id;
}
public int getType() {
return this.type;
}
public MoveType getDirection() {
return this.direction;
}
public boolean isAlive() {
return this.isAlive;
}
public EnemyDeath getDeathType() {
return this.deathType;
}
private MoveType getNewDirection(Point oldCoordinate, Point newCoordinate) {
if (newCoordinate.x > oldCoordinate.x) return MoveType.RIGHT;
if (newCoordinate.x < oldCoordinate.x) return MoveType.LEFT;
if (newCoordinate.y > oldCoordinate.y) return MoveType.DOWN;
if (newCoordinate.y < oldCoordinate.y) return MoveType.UP;
return null;
}
}

View file

@ -0,0 +1,18 @@
package io.riddles.hackman2.game.enemy;
/**
* io.riddles.hackman2.game.enemy.EnemyDeath - Created on 10-7-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public enum EnemyDeath {
ATTACK,
BOMBED;
@Override
public String toString() {
return this.name().toLowerCase();
}
}

View file

@ -0,0 +1,42 @@
package io.riddles.hackman2.game.enemy;
import org.json.JSONObject;
import io.riddles.hackman2.game.HackMan2ObjectSerializer;
import io.riddles.javainterface.serialize.Serializer;
/**
* io.riddles.hackman2.game.enemy.EnemySerializer - Created on 11-7-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class EnemySerializer implements Serializer<Enemy> {
@Override
public String traverseToString(Enemy enemy) {
return visitEnemy(enemy).toString();
}
@Override
public JSONObject traverseToJson(Enemy enemy) {
return visitEnemy(enemy);
}
private JSONObject visitEnemy(Enemy enemy) {
HackMan2ObjectSerializer objectSerializer = new HackMan2ObjectSerializer();
JSONObject enemyObj = objectSerializer.traverseToJson(enemy);
enemyObj.put("id", enemy.getId());
enemyObj.put("type", enemy.getType());
enemyObj.put("move", enemy.getDirection());
if (!enemy.isAlive()) {
enemyObj.put("death", enemy.getDeathType());
}
return enemyObj;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import io.riddles.hackman2.game.HackMan2Object;
/**
* io.riddles.hackman2.game.enemy.SpawnPoint - Created on 14-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class EnemySpawnPoint extends HackMan2Object {
private int type;
public Integer spawnTime;
public EnemySpawnPoint(Point coordinate, int type) {
super(coordinate);
this.type = type;
this.spawnTime = null;
}
@Override
public String toString() {
if (this.spawnTime == null || this.spawnTime == 0) {
return "S";
}
return String.format("S%d", this.spawnTime);
}
public void setSpawnTime(Integer spawnTime) {
this.spawnTime = spawnTime;
}
public void reduceSpawnTime() {
if (this.spawnTime == null) return;
this.spawnTime--;
}
public Enemy spawnEnemy(int id) {
if (this.spawnTime == null || this.spawnTime != 0) return null;
this.spawnTime = null;
return new Enemy(id, new Point(getCoordinate()), this.type);
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import java.util.ArrayList;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.FarChaseAI - Created on 8-6-17
*
* This enemy AI will always move to the current location
* of the farthest player.
*
* @author Jim van Eeden - jim@riddles.io
*/
public class FarChaseAI extends AbstractEnemyAI {
@Override
public Point transform(Enemy enemy, HackMan2State state) {
HackMan2Board board = state.getBoard();
Point coordinate = enemy.getCoordinate();
ArrayList<MoveType> availableDirections = getAvailableDirections(enemy, board);
Point mandatoryTransform = mandatoryTransform(enemy, availableDirections);
if (mandatoryTransform != null) { // Mandatory directions are always taken
return mandatoryTransform;
}
// Get farthest player
ArrayList<HackMan2PlayerState> players = getSortedPlayers(coordinate, state);
HackMan2PlayerState farthestPlayer = players.get(players.size() - 1);
return transformToGoal(coordinate, farthestPlayer.getCoordinate(), availableDirections);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.LeverAI - Created on 8-6-17
*
* This enemy AI will always move to a point that is the vector
* from the enemy closest to the closest player, to that closest player
* times two.
*
* @author Jim van Eeden - jim@riddles.io
*/
public class LeverAI extends AbstractEnemyAI {
@Override
public Point transform(Enemy enemy, HackMan2State state) {
HackMan2Board board = state.getBoard();
Point coordinate = enemy.getCoordinate();
ArrayList<MoveType> availableDirections = getAvailableDirections(enemy, board);
Point mandatoryTransform = mandatoryTransform(enemy, availableDirections);
if (mandatoryTransform != null) { // Mandatory directions are always taken
return mandatoryTransform;
}
HackMan2PlayerState closestPlayer = getSortedPlayers(coordinate, state).get(0);
ArrayList<Enemy> enemies = state.getBoard().getEnemies();
Enemy closestEnemy = enemies.stream()
.filter(e -> e.getId() != enemy.getId())
.min(Comparator.comparingDouble(
e -> e.getCoordinate().distance(closestPlayer.getCoordinate())))
.orElse(null);
if (closestEnemy == null) { // if no other enemy, take self
closestEnemy = enemy;
}
Point playerCoord = closestPlayer.getCoordinate();
Point enemyCoord = closestEnemy.getCoordinate();
Point leverVector = new Point(
playerCoord.x - enemyCoord.x,
playerCoord.y - enemyCoord.y
);
Point goalPosition = new Point(
playerCoord.x + leverVector.x,
playerCoord.y + leverVector.y
);
return transformToGoal(coordinate, goalPosition, availableDirections);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.enemy;
import java.awt.*;
import java.util.ArrayList;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
/**
* io.riddles.hackman2.game.enemy.PredictAI - Created on 8-6-17
*
* This enemy AI will always move to the point 4 steps ahead
* of the closest player. Doesn't take walls, etc into account,
* so very crude.
*
* @author Jim van Eeden - jim@riddles.io
*/
public class PredictAI extends AbstractEnemyAI {
@SuppressWarnings("FieldCanBeLocal")
private final int PREDICT_STEPS = 4;
@Override
public Point transform(Enemy enemy, HackMan2State state) {
HackMan2Board board = state.getBoard();
Point coordinate = enemy.getCoordinate();
ArrayList<MoveType> availableDirections = getAvailableDirections(enemy, board);
Point mandatoryTransform = mandatoryTransform(enemy, availableDirections);
if (mandatoryTransform != null) { // Mandatory directions are always taken
return mandatoryTransform;
}
// Get predicted position of closest player
HackMan2PlayerState closestPlayer = getSortedPlayers(coordinate, state).get(0);
Point playerCoordinate = closestPlayer.getCoordinate();
Point direction = closestPlayer.getDirection().getDirectionVector();
Point predictedPosition = new Point(
playerCoordinate.x + (direction.x * PREDICT_STEPS),
playerCoordinate.y + (direction.y * PREDICT_STEPS)
);
return transformToGoal(coordinate, predictedPosition, availableDirections);
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.item;
import java.awt.Point;
import io.riddles.hackman2.game.HackMan2Object;
/**
* io.riddles.hackman2.game.item.Bomb - Created on 9-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class Bomb extends HackMan2Object {
private Integer ticks;
public Bomb(Point coordinate, Integer ticks) {
super(coordinate);
this.ticks = ticks;
}
public Bomb(Bomb bomb) {
super(bomb);
this.ticks = bomb.ticks;
}
@Override
public String toString() {
if (this.ticks == null) {
return "B";
}
return String.format("B%d", this.ticks);
}
public void tick() {
if (this.ticks == null) return;
this.ticks--;
}
public Integer getTicks() {
return this.ticks;
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.item;
import java.awt.Point;
import io.riddles.hackman2.game.HackMan2Object;
/**
* io.riddles.hackman2.game.item.Snippet - Created on 12-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class Snippet extends HackMan2Object {
public Snippet(Point coordinate) {
super(coordinate);
}
public Snippet(Snippet snippet) {
super(snippet);
}
@Override
public String toString() {
return "C";
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.move;
/**
* io.riddles.hackman2.game.move.ActionType - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public enum ActionType {
CHARACTER,
MOVE;
@Override
public String toString() {
return this.name().toLowerCase();
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.move;
import io.riddles.javainterface.exception.InvalidInputException;
import io.riddles.javainterface.game.move.AbstractMove;
/**
* io.riddles.hackman2.game.move.HackMan2Move - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Move extends AbstractMove {
private MoveType type;
private Integer bombTicks;
public HackMan2Move(MoveType type) {
this.type = type;
this.bombTicks = null;
}
public HackMan2Move(MoveType type, int bombTicks) {
this.type = type;
this.bombTicks = bombTicks;
}
public HackMan2Move(InvalidInputException exception) {
super(exception);
}
public MoveType getMoveType() {
return this.type;
}
public Integer getBombTicks() {
return this.bombTicks;
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.move;
import io.riddles.hackman2.engine.HackMan2Engine;
import io.riddles.javainterface.exception.InvalidInputException;
import io.riddles.javainterface.serialize.Deserializer;
/**
* io.riddles.hackman2.game.move.HackMan2MoveDeserializer - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2MoveDeserializer implements Deserializer<HackMan2Move> {
@Override
public HackMan2Move traverse(String string) {
try {
return visitMove(string);
} catch (InvalidInputException ex) {
return new HackMan2Move(ex);
} catch (Exception ex) {
return new HackMan2Move(new InvalidInputException("Failed to parse action"));
}
}
private HackMan2Move visitMove(String input) throws InvalidInputException {
String[] split = input.split(";");
MoveType moveType = visitMoveType(split[0]);
if (split.length < 2) {
return new HackMan2Move(moveType);
}
int bombTicks = visitDropBomb(split[1]);
return new HackMan2Move(moveType, bombTicks);
}
private MoveType visitMoveType(String input) throws InvalidInputException {
MoveType moveType = MoveType.fromString(input);
if (moveType == null) {
throw new InvalidInputException("Move action isn't valid");
}
return moveType;
}
private int visitDropBomb(String input) throws InvalidInputException {
String[] split = input.split(" ");
if (!split[0].equals("drop_bomb")) {
throw new InvalidInputException("Secondary move can only be 'drop_bomb'");
}
int bombTicks;
try {
bombTicks = Integer.parseInt(split[1]);
} catch (Exception e) {
throw new InvalidInputException("Can't parse amount of bomb ticks");
}
int minTicks = HackMan2Engine.configuration.getInt("bombMinTicks");
int maxTicks = HackMan2Engine.configuration.getInt("bombMaxTicks");
if (bombTicks < minTicks || bombTicks > maxTicks) {
throw new InvalidInputException(String.format(
"Bomb ticks must be between %d and %d (inclusive)", minTicks, maxTicks));
}
return bombTicks;
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.move;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import io.riddles.hackman2.engine.HackMan2Engine;
/**
* io.riddles.hackman2.game.move.MoveType - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public enum MoveType {
UP,
DOWN,
LEFT,
RIGHT,
PASS;
private static final Map<String, MoveType> TYPE_MAP = new HashMap<>();
static {
for (MoveType moveType : values()) {
TYPE_MAP.put(moveType.toString(), moveType);
}
}
public static ArrayList<MoveType> getMovingMoveTypes() {
ArrayList<MoveType> movingMoveTypes = new ArrayList<>();
movingMoveTypes.add(UP);
movingMoveTypes.add(DOWN);
movingMoveTypes.add(LEFT);
movingMoveTypes.add(RIGHT);
return movingMoveTypes;
}
public MoveType getOppositeMoveType() {
switch (this) {
case LEFT:
return MoveType.RIGHT;
case RIGHT:
return MoveType.LEFT;
case UP:
return MoveType.DOWN;
case DOWN:
return MoveType.UP;
default:
return this;
}
}
public Point getDirectionVector() {
switch (this) {
case LEFT:
return new Point(-1, 0);
case RIGHT:
return new Point(1, 0);
case UP:
return new Point(0, -1);
case DOWN:
return new Point(0, 1);
default:
return new Point(0, 0);
}
}
public Point getCoordinateAfterMove(Point coordinate) {
Point direction = getDirectionVector();
return new Point(coordinate.x + direction.x, coordinate.y + direction.y);
}
public static MoveType fromString(String string) {
return TYPE_MAP.get(string.toLowerCase());
}
@Override
public String toString() {
return this.name().toLowerCase();
}
}

View file

@ -0,0 +1,41 @@
package io.riddles.hackman2.game.player;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import io.riddles.hackman2.engine.HackMan2Engine;
/**
* io.riddles.hackman2.game.player.PlayerType - Created on 10-7-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public enum CharacterType {
BIXIE,
BIXIETTE;
private static final Map<String, CharacterType> TYPE_MAP = new HashMap<>();
static {
for (CharacterType moveType : values()) {
TYPE_MAP.put(moveType.toString(), moveType);
}
}
public static CharacterType getRandomCharacter() {
ArrayList<CharacterType> values = new ArrayList<>(TYPE_MAP.values());
return values.get(HackMan2Engine.RANDOM.nextInt(values.size()));
}
public static CharacterType fromString(String string) {
return TYPE_MAP.get(string);
}
@Override
public String toString() {
return this.name().toLowerCase();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.player;
import io.riddles.javainterface.game.player.AbstractPlayer;
/**
* io.riddles.hackman2.game.player.HackMan2Player - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Player extends AbstractPlayer {
private CharacterType characterType;
public HackMan2Player(int id) {
super(id);
}
public void setCharacterType(CharacterType characterType) {
this.characterType = characterType;
}
public CharacterType getCharacterType() {
return this.characterType;
}
}

View file

@ -0,0 +1,191 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.processor;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import io.riddles.hackman2.engine.HackMan2Engine;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.hackman2.game.move.ActionType;
import io.riddles.hackman2.game.move.HackMan2Move;
import io.riddles.hackman2.game.move.HackMan2MoveDeserializer;
import io.riddles.hackman2.game.player.HackMan2Player;
import io.riddles.hackman2.game.state.HackMan2PlayerState;
import io.riddles.hackman2.game.state.HackMan2State;
import io.riddles.javainterface.exception.InvalidMoveException;
import io.riddles.javainterface.game.player.PlayerProvider;
import io.riddles.javainterface.game.processor.SimpleProcessor;
import io.riddles.javainterface.game.state.AbstractPlayerState;
/**
* io.riddles.hackman2.game.processor.HackMan2Processor - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2Processor extends SimpleProcessor<HackMan2State, HackMan2Player> {
private HackMan2MoveDeserializer moveDeserializer;
public HackMan2Processor(PlayerProvider<HackMan2Player> playerProvider) {
super(playerProvider);
this.moveDeserializer = new HackMan2MoveDeserializer();
}
@Override
public boolean hasGameEnded(HackMan2State state) {
int maxRounds = HackMan2Engine.configuration.getInt("maxRounds");
ArrayList<HackMan2PlayerState> alivePlayers = state.getAlivePlayers();
return state.getRoundNumber() >= maxRounds || alivePlayers.size() <= 1;
}
@Override
public Integer getWinnerId(HackMan2State state) {
ArrayList<HackMan2PlayerState> alivePlayers = state.getAlivePlayers();
if (alivePlayers.size() == 0) {
return null;
} else if (alivePlayers.size() == 1) {
return alivePlayers.get(0).getPlayerId();
}
ArrayList<HackMan2PlayerState> winners = new ArrayList<>();
int maxSnippets = -1;
for (HackMan2PlayerState playerState : alivePlayers) {
if (playerState.getSnippets() > maxSnippets) {
maxSnippets = playerState.getSnippets();
winners.clear();
winners.add(playerState);
} else if (playerState.getSnippets() == maxSnippets) {
winners.add(playerState);
}
}
if (winners.size() == 1) {
return winners.get(0).getPlayerId();
}
return null;
}
@Override
public double getScore(HackMan2State state) {
return state.getRoundNumber();
}
@Override
public HackMan2State createNextState(HackMan2State inputState, int roundNumber) {
HackMan2State nextState = inputState.createNextState(roundNumber);
HackMan2Board board = nextState.getBoard();
// Clean up enemies from previous round (i.e. remove dead ones)
board.cleanUpEnemies();
// Send updates and get all moves
for (HackMan2PlayerState playerState : nextState.getPlayerStates()) {
HackMan2Player player = getPlayer(playerState.getPlayerId());
sendUpdatesToPlayer(inputState, player);
HackMan2Move move = getPlayerMove(player);
playerState.setMove(move);
}
// Move enemies (before they know where the players are going)
board.moveEnemies(nextState);
// Move the players
executePlayerMoves(nextState);
// Perform object pickups
board.performPickups(nextState);
// Spawn enemies from spawnpoints (before collisions)
board.spawnEnemies();
// Calculate changes due to collisions
board.performCollisions(inputState, nextState);
// Spawn all new objects
board.spawnObjects(nextState);
return nextState;
}
private void sendUpdatesToPlayer(HackMan2State state, HackMan2Player player) {
player.sendUpdate("round", state.getRoundNumber());
player.sendUpdate("field", state.getBoard().toString(state));
for (HackMan2PlayerState targetPlayerState : state.getPlayerStates()) {
HackMan2Player target = getPlayer(targetPlayerState.getPlayerId());
player.sendUpdate("snippets", target, targetPlayerState.getSnippets());
player.sendUpdate("bombs", target, targetPlayerState.getBombs());
}
}
private void executePlayerMoves(HackMan2State state) {
HackMan2Board board = state.getBoard();
for (HackMan2PlayerState playerState : state.getPlayerStates()) {
HackMan2Move move = playerState.getMove();
board.validateMove(playerState, move);
if (!move.isInvalid()) {
executePlayerMove(board, playerState, move);
}
if (move.isInvalid()) {
HackMan2Player player = getPlayer(playerState.getPlayerId());
player.sendWarning(move.getException().getMessage());
}
}
}
private void executePlayerMove(HackMan2Board board, HackMan2PlayerState playerState, HackMan2Move move) {
// Move player
Point oldCoordinate = playerState.getCoordinate();
Point newCoordinate = board.getCoordinateAfterMove(move.getMoveType(), oldCoordinate);
playerState.setCoordinate(newCoordinate);
playerState.setDirection(move.getMoveType());
// Drop bomb
if (move.getBombTicks() != null) {
if (playerState.getBombs() > 0) {
board.dropBomb(newCoordinate, move.getBombTicks() + 1); // +1 for tick this round
playerState.updateBombs(-1);
} else {
move.setException(new InvalidMoveException("No bombs available"));
}
}
}
private HackMan2Move getPlayerMove(HackMan2Player player) {
String response = player.requestMove(ActionType.MOVE);
return this.moveDeserializer.traverse(response);
}
private HackMan2Player getPlayer(int id) {
return this.playerProvider.getPlayerById(id);
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.state;
import java.awt.*;
import io.riddles.hackman2.game.move.HackMan2Move;
import io.riddles.hackman2.game.move.MoveType;
import io.riddles.javainterface.game.state.AbstractPlayerState;
/**
* io.riddles.hackman2.game.state.HackMan2PlayerState - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2PlayerState extends AbstractPlayerState<HackMan2Move> {
private int snippets;
private int bombs;
private Point coordinate;
private boolean isAlive;
private MoveType direction;
public HackMan2PlayerState(int playerId, Point startCoordinate) {
super(playerId);
this.snippets = 0;
this.bombs = 0;
this.coordinate = startCoordinate;
this.isAlive = true;
this.direction = null;
}
public HackMan2PlayerState(HackMan2PlayerState playerState) {
super(playerState.getPlayerId());
this.snippets = playerState.getSnippets();
this.bombs = playerState.getBombs();
this.coordinate = new Point(playerState.getCoordinate());
this.isAlive = playerState.isAlive();
this.direction = playerState.getDirection();
}
public String toString() {
return String.format("P%d", this.playerId);
}
public void updateSnippets(int delta) {
this.snippets += delta;
if (this.snippets < 0) {
this.snippets = 0;
this.isAlive = false;
}
}
public int getSnippets() {
return this.snippets;
}
public void updateBombs(int delta) {
this.bombs = this.bombs + delta >= 0 ? this.bombs + delta : 0;
}
public int getBombs() {
return this.bombs;
}
public void setCoordinate(Point coordinate) {
this.coordinate = coordinate;
}
public Point getCoordinate() {
return this.coordinate;
}
public boolean isAlive() {
return this.isAlive;
}
public void setDirection(MoveType direction) {
this.direction = direction;
}
public MoveType getDirection() {
return this.direction;
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.state;
import org.json.JSONObject;
import io.riddles.javainterface.serialize.Serializer;
/**
* io.riddles.hackman2.game.state.HackMan2PlayerStateSerializer - Created on 15-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2PlayerStateSerializer implements Serializer<HackMan2PlayerState> {
@Override
public String traverseToString(HackMan2PlayerState playerState) {
return visitPlayerState(playerState).toString();
}
@Override
public JSONObject traverseToJson(HackMan2PlayerState playerState) {
return visitPlayerState(playerState);
}
private JSONObject visitPlayerState(HackMan2PlayerState playerState) {
JSONObject playerStateObj = new JSONObject();
playerStateObj.put("id", playerState.getPlayerId());
playerStateObj.put("x", playerState.getCoordinate().x);
playerStateObj.put("y", playerState.getCoordinate().y);
playerStateObj.put("bombs", playerState.getBombs());
playerStateObj.put("score", playerState.getSnippets());
if (playerState.getMove() != null && !playerState.getMove().isInvalid()) {
playerStateObj.put("move", playerState.getMove().getMoveType());
} else {
playerStateObj.put("move", JSONObject.NULL);
}
return playerStateObj;
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.state;
import java.util.ArrayList;
import java.util.stream.Collectors;
import io.riddles.hackman2.game.board.HackMan2Board;
import io.riddles.javainterface.game.state.AbstractPlayerState;
import io.riddles.javainterface.game.state.AbstractState;
/**
* io.riddles.hackman2.game.state.HackMan2State - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2State extends AbstractState<HackMan2PlayerState> {
private HackMan2Board board;
private int snippetsCollected;
// For initital state only
public HackMan2State(ArrayList<HackMan2PlayerState> playerStates, HackMan2Board board) {
super(null, playerStates, 0);
this.board = board;
this.snippetsCollected = 0;
}
public HackMan2State(HackMan2State previousState, ArrayList<HackMan2PlayerState> playerStates, int roundNumber) {
super(previousState, playerStates, roundNumber);
this.board = new HackMan2Board(previousState.board);
this.snippetsCollected = previousState.snippetsCollected;
}
public HackMan2State createNextState(int roundNumber) {
// Create new player states from current player states
ArrayList<HackMan2PlayerState> playerStates = new ArrayList<>();
for (HackMan2PlayerState playerState : this.getPlayerStates()) {
playerStates.add(new HackMan2PlayerState(playerState));
}
// Create new state from current state
return new HackMan2State(this, playerStates, roundNumber);
}
public ArrayList<HackMan2PlayerState> getAlivePlayers() {
return this.playerStates.stream()
.filter(HackMan2PlayerState::isAlive)
.collect(Collectors.toCollection(ArrayList::new));
}
public void addSnippetCollected() {
this.snippetsCollected++;
}
public int getSnippetsCollected() {
return this.snippetsCollected;
}
public HackMan2Board getBoard() {
return this.board;
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.state;
import org.json.JSONArray;
import org.json.JSONObject;
import io.riddles.hackman2.game.HackMan2ObjectSerializer;
import io.riddles.hackman2.game.enemy.EnemySerializer;
import io.riddles.hackman2.game.item.Bomb;
import io.riddles.hackman2.game.item.Snippet;
import io.riddles.javainterface.serialize.Serializer;
/**
* io.riddles.hackman2.game.state.HackMan2StateSerializer - Created on 8-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
public class HackMan2StateSerializer implements Serializer<HackMan2State> {
@Override
public String traverseToString(HackMan2State state) {
return visitState(state).toString();
}
@Override
public JSONObject traverseToJson(HackMan2State state) {
return visitState(state);
}
private JSONObject visitState(HackMan2State state) {
HackMan2PlayerStateSerializer playerStateSerializer = new HackMan2PlayerStateSerializer();
EnemySerializer enemySerializer = new EnemySerializer();
HackMan2ObjectSerializer objectSerializer = new HackMan2ObjectSerializer();
JSONObject stateObj = new JSONObject();
JSONArray playerArray = new JSONArray();
JSONArray collectibleArray = new JSONArray();
JSONArray bombArray = new JSONArray();
JSONArray enemyArray = new JSONArray();
state.getPlayerStates().forEach(
playerState -> playerArray.put(playerStateSerializer.traverseToJson(playerState))
);
state.getBoard().getSnippets().forEach(
(key, snippet) -> collectibleArray.put(objectSerializer.traverseToJson(snippet))
);
state.getBoard().getBombs().forEach((key, bomb) -> {
JSONObject bombObj = objectSerializer.traverseToJson(bomb);
bombObj.put("ticks", bomb.getTicks());
bombArray.put(bombObj);
});
state.getBoard().getEnemies().forEach(
enemy -> enemyArray.put(enemySerializer.traverseToJson(enemy))
);
stateObj.put("round", state.getRoundNumber());
stateObj.put("players", playerArray);
stateObj.put("collectibles", collectibleArray);
stateObj.put("bombs", bombArray);
stateObj.put("enemies", enemyArray);
return stateObj;
}
}

View file

@ -0,0 +1,60 @@
package io.riddles.hackman2.engine
import io.riddles.hackman2.game.player.HackMan2Player
import io.riddles.hackman2.game.state.HackMan2PlayerState
import io.riddles.hackman2.game.state.HackMan2State
import io.riddles.javainterface.game.player.PlayerProvider
import io.riddles.javainterface.io.FileIOHandler
import spock.lang.Specification
/**
* io.riddles.hackman2.engine.HackMan2EngineSpec - Created on 3-10-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
class HackMan2EngineSpec extends Specification {
class TestEngine extends HackMan2Engine {
TestEngine(String wrapperInput, String[] botFiles) {
super(new PlayerProvider<>(), new FileIOHandler(wrapperInput))
for (int i = 0; i < botFiles.length; i++) {
HackMan2Player player = new HackMan2Player(i)
player.setIoHandler(new FileIOHandler(botFiles[i]))
this.playerProvider.add(player)
}
this.processor = createProcessor()
}
}
def "test gates"() {
setup:
String[] botInputs = new String[2]
String wrapperInput = "./test/resources/wrapper_input.txt"
botInputs[0] = "./test/resources/bot1_test_gates_input.txt"
botInputs[1] = "./test/resources/bot2_test_gates_input.txt"
TestEngine engine = new TestEngine(wrapperInput, botInputs)
TestEngine.configuration = engine.getDefaultConfiguration()
TestEngine.configuration.put("maxRounds", 13)
HackMan2State initialState = engine.getInitialState()
when:
HackMan2State finalState = engine.run(initialState)
HackMan2PlayerState playerState1 = finalState.getPlayerStateById(0)
HackMan2PlayerState playerState2 = finalState.getPlayerStateById(1)
then:
playerState1.getCoordinate().x == 17
playerState1.getCoordinate().y == 6
playerState2.getCoordinate().x == 1
playerState2.getCoordinate().y == 7
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2017 riddles.io (developers@riddles.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package io.riddles.hackman2.game.board
import io.riddles.hackman2.game.item.Bomb
import spock.lang.Specification
import java.awt.Point
/**
* io.riddles.hackman2.game.board.HackMan2BoardSpec - Created on 13-6-17
*
* [description]
*
* @author Jim van Eeden - jim@riddles.io
*/
class HackMan2BoardSpec extends Specification {
String layout = ".,.,.,x,.,.,.,.,.,.,.,.,.,.,.,x,.,.,.," +
".,x,.,x,.,x,x,x,x,.,x,x,x,x,.,x,.,x,.," +
".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," +
".,x,x,x,.,x,.,x,x,x,x,x,.,x,.,x,x,x,.," +
".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," +
".,.,.,x,.,x,.,x,x,.,x,x,.,x,.,x,.,.,.," +
"x,.,x,x,.,.,.,x,x,.,x,x,.,.,.,x,x,.,x," +
".,.,x,x,.,x,x,x,x,.,x,x,x,x,.,x,x,.,.," +
"x,.,x,x,.,.,.,.,.,.,.,.,.,.,.,x,x,.,x," +
".,.,.,x,.,x,x,x,x,x,x,x,x,x,.,x,.,.,.," +
".,x,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,x,.," +
".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," +
".,x,.,x,x,.,x,x,x,x,x,x,x,.,x,x,.,x,.," +
".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," +
".,.,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,.,."
def "test explode bombs"() {
setup:
HackMan2Board board = new HackMan2Board(19, 15, layout, null, new ArrayList<>())
board.dropBomb(new Point(0, 0), 0)
board.dropBomb(new Point(2, 0), 5)
board.dropBomb(new Point(2, 1), 2)
board.dropBomb(new Point(2, 2), 0)
board.dropBomb(new Point(18, 0), 0)
Point coordinate = new Point(18, 5)
board.bombs.put(coordinate.toString(), new Bomb(coordinate, null))
when:
ArrayList<String> explosions = board.explodeBombs()
then:
explosions.toString() == "[java.awt.Point[x=0,y=0], java.awt.Point[x=1,y=0], " +
"java.awt.Point[x=2,y=0], java.awt.Point[x=2,y=1], java.awt.Point[x=2,y=2], " +
"java.awt.Point[x=3,y=2], java.awt.Point[x=4,y=2], java.awt.Point[x=0,y=1], " +
"java.awt.Point[x=0,y=2], java.awt.Point[x=0,y=3], java.awt.Point[x=0,y=4], " +
"java.awt.Point[x=0,y=5], java.awt.Point[x=18,y=0], java.awt.Point[x=18,y=1], " +
"java.awt.Point[x=18,y=2], java.awt.Point[x=18,y=3], java.awt.Point[x=18,y=4], " +
"java.awt.Point[x=18,y=5], java.awt.Point[x=17,y=0], java.awt.Point[x=16,y=0]]"
board.getBombs().size() == 1
}
}

View file

@ -0,0 +1,20 @@
bixie
up
up
up
left
left
down
left
down
down
left
left
left
up
up
right
up
up
up
up

View file

@ -0,0 +1,20 @@
bixie
pass
down
down
down
right
right
up
right
up
up
right
right
right
down
down
left
down
down
down

View file

@ -0,0 +1,2 @@
details
game

View file

@ -0,0 +1,16 @@
settings player_names player0,player1
settings your_bot player0
settings timebank 5000
settings time_per_move 200
settings your_botid 0
settings field_width 19
settings field_height 15
settings max_rounds 250
action character 5000
update game round 0
update game field S,.,.,x,.,.,.,.,.,.,.,.,.,.,.,x,.,.,S,.,x,.,x,.,x,x,x,x,.,x,x,x,x,.,x,.,x,.,C,x,.,.,.,x,.,.,.,C,.,.,.,x,.,.,.,x,.,.,x,x,x,.,x,.,x,x,x,x,x,.,x,.,x,x,x,.,.,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.,.,.,.,x,.,x,.,x,x,.,x,x,.,x,.,x,.,.,.,x,.,x,x,.,.,.,x,x,.,x,x,.,.,.,x,x,.,x,Gl,.,x,x,P0,x,x,x,x,.,x,x,x,x,P1,x,x,.,Gr,x,.,x,x,.,.,.,.,.,.,.,.,.,.,.,x,x,.,x,.,.,.,x,.,x,x,x,x,x,x,x,x,x,.,x,.,.,.,.,x,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,x,.,.,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.,.,x,.,x,x,.,x,x,x,x,x,x,x,.,x,x,.,x,.,.,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.,S,.,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,.,S
update player0 snippets 0
update player0 bombs 0
update player1 snippets 0
update player1 bombs 0
action move 5000

View file

@ -0,0 +1,21 @@
{
"wrapper": {
"timebankMax": 2000,
"timePerMove": 100,
"maxTimeouts": 0,
"resultFile": "./resultfile.json",
"propagateBotExitCode": false,
"debug": true
},
"match": {
"bots": [{
"command": "java -jar /home/jim/workspace/hackman2-starterbot-java/build/libs/hackman2-starterbot-java-1.0.0.jar"
},{
"command": "java -jar /home/jim/workspace/hackman2-starterbot-java/build/libs/hackman2-starterbot-java-1.0.0.jar"
}],
"engine": {
"command": "java -jar /home/jim/workspace/hackman2-engine-java/build/libs/hackman2-engine-java-1.0.3.jar",
"configuration": {}
}
}
}