Skip to content

Commit 2f20af2

Browse files
committed
Merge refactor into master
2 parents 36e489f + 5f60233 commit 2f20af2

File tree

108 files changed

+15213
-1031
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+15213
-1031
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
server2.conf
12
*.log
23

34
.idea/

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ EXPOSE 42638
44

55
WORKDIR /server2/
66
ADD games-server/build/libs/*-all.jar /server2/
7+
ADD server2.conf.docker /server2/server2.conf
78

89
VOLUME /data/logs/
910

10-
CMD java -jar /server2/games-server-1.0-SNAPSHOT-all.jar -httpPort 42638
11+
CMD java -jar /server2/games-server-1.0-SNAPSHOT-all.jar

Jenkinsfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,18 @@ pipeline {
77
agent any
88

99
stages {
10-
stage('Prepare') {
11-
steps {
12-
checkout scm
13-
}
14-
}
1510
stage('Build') {
1611
steps {
1712
sh 'cp /home/zomis/jenkins/server2-secrets.properties games-server/src/main/resources/secrets.properties'
13+
sh 'cp /home/zomis/jenkins/server2-startup.conf server2.conf.docker'
1814
sh './gradlew clean test :games-server:assemble :games-js:assemble'
1915
script {
2016
def gitChanges = sh(script: 'git diff-index HEAD', returnStatus: true)
2117
if (gitChanges) {
2218
error("There are git changes after build")
2319
}
2420
}
21+
sh 'cp games-js/.eslintrc.js games-js/web/'
2522
dir('games-vue-client') {
2623
sh 'npm install && npm run build'
2724
}

games-core/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
buildscript {
2-
ext.kotlin_version = '1.2.30'
2+
ext.kotlin_version = '1.3.31'
33
ext.junitJupiterVersion = '5.0.2'
44

55
repositories {
@@ -20,11 +20,12 @@ sourceCompatibility = 1.6
2020
repositories {
2121
mavenCentral()
2222
maven { url "http://www.zomis.net/maven" }
23+
maven { url 'https://jitpack.io' } // KLogging
2324
}
2425

2526
dependencies {
2627
compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
27-
compile "com.github.lewik.klogging:klogging.common:1.2.30"
28+
compile "com.github.lewik.klog:klog-metadata:1.3.31"
2829
compile "net.zomis:uttt-common:0.3.0"
2930

3031
testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package net.zomis
2+
3+
class Best<T>(private val valueFunction: (T) -> Double) {
4+
5+
private var bestValue: Double = Double.NEGATIVE_INFINITY
6+
private var bestElements: MutableList<T> = mutableListOf()
7+
8+
fun next(element: T) {
9+
val value = valueFunction(element)
10+
if (value > bestValue) {
11+
bestValue = value
12+
bestElements = mutableListOf(element)
13+
} else if (value >= bestValue) {
14+
bestElements.add(element)
15+
}
16+
}
17+
18+
fun asCollection(): Collection<T> = bestElements.toList()
19+
20+
fun randomBest(): T = bestElements.random()
21+
fun getBest(): List<T> = bestElements.toList()
22+
fun firstBest(): T = bestElements.first()
23+
fun isBest(element: T): Boolean = bestElements.contains(element)
24+
fun getBestValue(): Double = bestValue
25+
26+
}

games-core/src/main/kotlin/net/zomis/core/events/EventSystem.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package net.zomis.core.events
22

3-
import klogging.KLoggers
3+
import klog.KLoggers
44
import kotlin.reflect.KClass
55

66
enum class ListenerPriority { FIRST, EARLIER, EARLY, NORMAL, LATE, LATER, LAST }
@@ -34,14 +34,14 @@ class ListenerList<E> {
3434
}
3535

3636
fun execute(event: E) {
37-
list.forEach({
37+
list.forEach {
3838
try {
3939
it.run(event)
4040
} catch (e: RuntimeException) {
4141
logger.error(e, "Problem when handling event $event in $it")
4242
throw e
4343
}
44-
})
44+
}
4545
}
4646

4747
}
@@ -54,7 +54,7 @@ open class EventSystem {
5454
fun <E : Any> listen(description: String, priority: ListenerPriority, clazz: KClass<E>,
5555
condition: (E) -> Boolean, handler: EventHandler<E>) {
5656
logger.info("Add Listener \"$description\" with priority $priority to $clazz: $handler")
57-
val list: ListenerList<E> = listeners.getOrElse(clazz as KClass<Any>, { ListenerList<E>() }) as ListenerList<E>
57+
val list: ListenerList<E> = listeners.getOrElse(clazz as KClass<Any>) { ListenerList<E>() } as ListenerList<E>
5858
list.add(Listener(description, priority, condition, handler))
5959
listeners[clazz] = list as ListenerList<Any>
6060
}

games-core/src/main/kotlin/net/zomis/games/Features.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package net.zomis.games
22

3-
import klogging.KLoggers
3+
import klog.KLoggers
44
import net.zomis.core.events.EventSystem
55
import kotlin.reflect.KClass
66

@@ -17,7 +17,7 @@ class Features(val events: EventSystem?) {
1717
val data = mutableSetOf<Any>()
1818

1919
operator fun <T: Any> get(clazz: KClass<T>): T? {
20-
val value = data.find({ clazz.isInstance(it) })
20+
val value = data.find { clazz.isInstance(it) }
2121
return value as T?
2222
}
2323

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package net.zomis.games
2+
3+
import net.zomis.Best
4+
5+
private fun flipX(start: Position): Position {
6+
return Position(start.sizeX - 1 - start.x, start.y, start.sizeX, start.sizeY)
7+
}
8+
private fun flipY(start: Position): Position {
9+
return Position(start.x, start.sizeY - 1 - start.y, start.sizeX, start.sizeY)
10+
}
11+
private fun rotate(start: Position): Position {
12+
return Position(start.sizeY - 1 - start.y, start.x, start.sizeY, start.sizeX)
13+
}
14+
15+
data class Position(val x: Int, val y: Int, val sizeX: Int, val sizeY: Int) {
16+
fun next(): Position? {
17+
if (x == sizeX - 1) {
18+
return if (y == sizeY - 1) null else Position(0, y + 1, sizeX, sizeY)
19+
}
20+
return Position(this.x + 1, this.y, this.sizeX, this.sizeY)
21+
}
22+
}
23+
24+
enum class TransformationType(val transforming: (Position) -> Position, val reverse: (Position) -> Position) {
25+
FLIP_X(::flipX, ::flipX),
26+
FLIP_Y(::flipY, ::flipY),
27+
ROTATE(::rotate, { start -> rotate(rotate(rotate(start))) }),
28+
;
29+
}
30+
31+
enum class Transformation(private val transformations: List<TransformationType>) {
32+
NO_CHANGE(listOf()),
33+
FLIP_X(listOf(TransformationType.FLIP_X)),
34+
FLIP_Y(listOf(TransformationType.FLIP_Y)),
35+
ROTATE_90(listOf(TransformationType.ROTATE)),
36+
ROTATE_180(listOf(TransformationType.ROTATE, TransformationType.ROTATE)),
37+
ROTATE_270(listOf(TransformationType.ROTATE, TransformationType.ROTATE, TransformationType.ROTATE)),
38+
ROTATE_90_FLIP_X(listOf(TransformationType.ROTATE, TransformationType.FLIP_X)),
39+
ROTATE_90_FLIP_Y(listOf(TransformationType.ROTATE, TransformationType.FLIP_Y)),
40+
;
41+
42+
fun transform(position: Position): Position {
43+
return transformations.fold(position) { pos, trans -> trans.transforming(pos) }
44+
}
45+
46+
fun reverseTransform(position: Position): Position {
47+
return transformations.reversed().fold(position) { pos, trans -> trans.reverse(pos) }
48+
}
49+
50+
private val referencePoints = arrayOf(
51+
Position(3, 2, 5, 5),
52+
Position(4, 0, 5, 5)
53+
)
54+
fun apply(transformation: Transformation): Transformation {
55+
val simplestTransformation = Transformation.values().filter {result ->
56+
referencePoints.all {p -> transformation.transform(this.transform(p)) == result.transform(p) }
57+
}
58+
return simplestTransformation.single()
59+
}
60+
61+
}
62+
63+
class Map2D<T>(val sizeX: Int, val sizeY: Int, val getter: (x: Int, y: Int) -> T, val setter: (x: Int, y: Int, value: T) -> Unit) {
64+
65+
fun standardizedTransformation(valueFunction: (T) -> Int): Transformation {
66+
// keep a Set<Transformation>, start with all of them
67+
// loop through Transformations and find ones with the extremes
68+
// the goal is that of all the possible transformations, the result should be the one with the lowest/highest value
69+
70+
// start in the possible fields for the target map upper-left corner
71+
// then continue, line by line, beginning with increasing X and then increase Y
72+
val possibleTransformations = Transformation.values().toMutableSet()
73+
if (sizeX != sizeY) {
74+
// Rotating 90 or 270 degrees only works if both width or height is the same
75+
possibleTransformations.remove(Transformation.ROTATE_90)
76+
possibleTransformations.remove(Transformation.ROTATE_270)
77+
}
78+
79+
var position: Position? = Position(0, 0, sizeX, sizeY)
80+
while (possibleTransformations.size > 1 && position != null) {
81+
val best = Best<Transformation> { transformation ->
82+
val originalPos = transformation.reverseTransform(position!!)
83+
val originalT = getter(originalPos.x, originalPos.y)
84+
valueFunction(originalT).toDouble()
85+
}
86+
possibleTransformations.forEach {
87+
best.next(it)
88+
}
89+
possibleTransformations.retainAll(best.getBest())
90+
91+
position = position.next()
92+
}
93+
94+
val transformation = possibleTransformations.first() // map can be symmetric so don't use .single
95+
return transformation
96+
}
97+
98+
fun transform(transformation: Transformation) {
99+
val rotated = Map2DX(sizeX, sizeY) { x, y ->
100+
val pos = Position(x, y, sizeX, sizeY)
101+
val oldPos = transformation.reverseTransform(pos)
102+
getter(oldPos.x, oldPos.y)
103+
}
104+
105+
(0 until sizeY).forEach {y ->
106+
(0 until sizeX).forEach {x ->
107+
setter(x, y, rotated.grid[y][x])
108+
}
109+
}
110+
}
111+
112+
fun standardize(valueFunction: (T) -> Int) {
113+
this.transform(this.standardizedTransformation(valueFunction))
114+
}
115+
116+
}
117+
118+
class Map2DX<T>(val sizeX: Int, val sizeY: Int, val factory: (x: Int, y: Int) -> T) {
119+
120+
val grid: MutableList<MutableList<T>> = (0 until sizeY).map { y ->
121+
(0 until sizeX).map { x ->
122+
factory(x, y)
123+
}.toMutableList()
124+
}.toMutableList()
125+
126+
fun set(x: Int, y: Int, value: T) {
127+
grid[y][x] = value
128+
}
129+
130+
fun asMap2D(): Map2D<T> {
131+
return Map2D(sizeX, sizeY, { x, y -> grid[y][x] }) {x, y, v ->
132+
grid[y][x] = v
133+
}
134+
}
135+
136+
fun standardize(value: (T) -> Int): Map2DX<T> {
137+
asMap2D().standardize(value)
138+
return this
139+
}
140+
141+
override fun toString(): String {
142+
return "Map2D(grid=$grid)"
143+
}
144+
145+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package net.zomis.games.dsl
2+
3+
import kotlin.reflect.KClass
4+
5+
data class Point(val x: Int, val y: Int)
6+
interface Actionable<T : Any, A : Any> {
7+
val playerIndex: Int
8+
val game: T
9+
val parameter: A
10+
}
11+
data class Action2D<T : Any, P>(override val game: T, override val playerIndex: Int,
12+
val x: Int, val y: Int, val target: P): Actionable<T, Point> {
13+
override val parameter = Point(x, y)
14+
}
15+
data class Action<T : Any, A : Any>(override val game: T, override val playerIndex: Int,
16+
override val parameter: A): Actionable<T, A>
17+
18+
typealias PlayerIndex = Int?
19+
fun PlayerIndex.isObserver(): Boolean = this == null
20+
21+
typealias GameSpec<T> = GameDsl<T>.() -> Unit
22+
typealias GameModelDsl<T, C> = GameModel<T, C>.() -> Unit
23+
typealias GameLogicDsl<T> = GameLogic<T>.() -> Unit
24+
typealias GameViewDsl<T> = GameView<T>.() -> Unit
25+
typealias GridDsl<T, P> = GameGrid<T, P>.() -> Unit
26+
27+
interface GameGrid<T, P> {
28+
val model: T
29+
fun size(sizeX: Int, sizeY: Int)
30+
fun getter(getter: (x: Int, y: Int) -> P)
31+
}
32+
33+
interface GridSpec<T, P> {
34+
val sizeX: (T) -> Int
35+
val sizeY: (T) -> Int
36+
fun get(model: T, x: Int, y: Int): P
37+
}
38+
39+
interface GameDsl<T : Any> {
40+
fun <P> gridSpec(spec: GridDsl<T, P>): GridDsl<T, P>
41+
fun <C : Any> setup(configClass: KClass<C>, modelDsl: GameModelDsl<T, C>)
42+
fun logic(logicDsl: GameLogicDsl<T>)
43+
fun view(viewDsl: GameViewDsl<T>)
44+
}
45+
46+
fun <T : Any> createGame(name: String, dsl: GameDsl<T>.() -> Unit): GameSpec<T> {
47+
return dsl
48+
}
49+
50+
data class ActionType<A>(val name: String)
51+
fun <A : Any> createActionType(name: String, parameterType: KClass<A>): ActionType<A> {
52+
return ActionType<A>(name)
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package net.zomis.games.dsl
2+
3+
import net.zomis.tttultimate.TTBase
4+
import net.zomis.tttultimate.TTFactories
5+
import net.zomis.tttultimate.TTPlayer
6+
import net.zomis.tttultimate.games.TTClassicController
7+
import net.zomis.tttultimate.games.TTController
8+
9+
data class TTOptions(val m: Int, val n: Int, val k: Int)
10+
fun TTPlayer.index(): Int {
11+
return when (this) {
12+
TTPlayer.X -> 0
13+
TTPlayer.O -> 1
14+
TTPlayer.NONE -> -1
15+
TTPlayer.XO -> -1
16+
}
17+
}
18+
19+
class DslTTT {
20+
val playAction = createActionType<Point>("play", Point::class)
21+
val game = createGame<TTController>("TTT") {
22+
val grid = gridSpec<TTBase> {
23+
size(model.game.sizeX, model.game.sizeY)
24+
getter { x, y -> model.game.getSub(x, y)!! }
25+
}
26+
setup(TTOptions::class) {
27+
defaultConfig {
28+
TTOptions(3, 3, 3)
29+
}
30+
init {conf ->
31+
TTClassicController(TTFactories().classicMNK(conf!!.m, conf.n, conf.k))
32+
}
33+
}
34+
logic {
35+
action2D(playAction, grid) {
36+
allowed { it.playerIndex == it.game.currentPlayer.index() && it.game.isAllowedPlay(it.target) }
37+
effect {
38+
it.game.play(it.target)
39+
}
40+
}
41+
}
42+
view {
43+
currentPlayer { it.currentPlayer.index() }
44+
winner { if (it.isGameOver) it.wonBy.index() else null }
45+
grid("board", grid) {
46+
owner { it.wonBy.index().takeIf {n -> n >= 0 } }
47+
}
48+
}
49+
}
50+
51+
}

0 commit comments

Comments
 (0)