@@ -3,6 +3,8 @@ package net.zomis.games.server2
33import kotlinx.coroutines.*
44import net.zomis.aiscores.*
55import net.zomis.fight.ext.Fight
6+ import net.zomis.fight.ext.WinResult
7+ import net.zomis.fights.Fights
68import net.zomis.games.ais.AlphaBeta
79import net.zomis.games.ais.Best
810import net.zomis.scorers.*
@@ -167,19 +169,16 @@ class TTT3D {
167169 return " Player $currentPlayer Fields ${allFields().toList()} "
168170 }
169171
170- }
171-
172- class TTT3DScorer (private val game : TTT3D ): ScoreStrategy<TTT3DPiece, TTT3DPoint> {
173- override fun canScoreField (parameters : ScoreParameters <TTT3DPiece >? , field : TTT3DPoint ? ): Boolean {
174- return canScore(parameters!! .parameters, field!! )
172+ fun isGameOver (): Boolean {
173+ return findWinner() != null || isDraw()
175174 }
176175
177- fun canScore ( player : TTT3DPiece , field : TTT3DPoint ): Boolean {
178- return game.currentPlayer == player && game.canPlayAt(field)
179- }
176+ }
177+
178+ class TTT3DScorer {
180179
181- fun canScore (player : ScoreParams <TTT3DPiece >, field : TTT3DPoint ): Boolean {
182- return game.currentPlayer == player. param && game .canPlayAt(field)
180+ fun canScore (state : ScoreParams <TTT3D >, field : TTT3DPoint ): Boolean {
181+ return state. param.canPlayAt(field)
183182 }
184183
185184 fun fieldsToScoreInGame (state : TTT3D ): Sequence <TTT3DPoint > {
@@ -188,20 +187,17 @@ class TTT3DScorer(private val game: TTT3D): ScoreStrategy<TTT3DPiece, TTT3DPoint
188187 }.asSequence()
189188 }
190189
191- override fun getFieldsToScore (parameters : TTT3DPiece ? ): MutableCollection <TTT3DPoint > {
192- if (game.currentPlayer != parameters) {
193- return mutableListOf ()
194- }
195- return fieldsToScoreInGame(game).toMutableList()
190+ fun getFieldsToScore (state : TTT3D ): MutableCollection <TTT3DPoint > {
191+ return fieldsToScoreInGame(state).toMutableList()
196192 }
197193
198194}
199195
200- class TTT3DIO ( private val game : TTT3D ) {
201- val scorerStrategy = TTT3DScorer (game )
202- val scorers = Scorers < TTT3DPiece , TTT3DPoint > (scorerStrategy::getFieldsToScore, scorerStrategy::canScore)
203- val scorerWant = NamedScorer <TTT3DPiece , TTT3DPoint >(" want" ) { piece , point ->
204- game. winConditions.filter { it.contains(point) }.filter { it.canWin(piece .param) }.map {
196+ class TTT3DIO {
197+ val scorerStrategy = TTT3DScorer ()
198+ val scorers = Scorers (scorerStrategy::getFieldsToScore, scorerStrategy::canScore)
199+ val scorerWant = NamedScorer <TTT3D , TTT3DPoint >(" want" ) { params , point ->
200+ params.param. winConditions.filter { it.contains(point) }.filter { it.canWin(params .param.currentPlayer ) }.map {
205201 when (it.emptySpaces()) {
206202 1 -> 100.0
207203 2 -> 1.0
@@ -210,8 +206,8 @@ class TTT3DIO(private val game: TTT3D) {
210206 }
211207 }.sum()
212208 }
213- val scorerSabotage = NamedScorer <TTT3DPiece , TTT3DPoint >(" sabotage" ) { piece , point ->
214- game. winConditions.filter { it.contains(point) }.filter { it.canWin(piece .param.opponent()) }.map {
209+ val scorerSabotage = NamedScorer <TTT3D , TTT3DPoint >(" sabotage" ) { params , point ->
210+ params.param. winConditions.filter { it.contains(point) }.filter { it.canWin(params .param.currentPlayer .opponent()) }.map {
215211 when (it.emptySpaces()) {
216212 1 -> 50.0
217213 2 -> 1.0
@@ -220,22 +216,22 @@ class TTT3DIO(private val game: TTT3D) {
220216 }
221217 }.sum()
222218 }
223- val alerts = NamedScorer <TTT3DPiece , TTT3DPoint >(" alerts" ) { piece , point ->
224- val block = scorerSabotage.scoring(piece , point) > 30
225- val doNotPlace = nextLevelReveal.scoring(piece , point) < - 30
219+ val alerts = NamedScorer <TTT3D , TTT3DPoint >(" alerts" ) { params , point ->
220+ val block = scorerSabotage.scoring(params , point) > 30
221+ val doNotPlace = nextLevelReveal.scoring(params , point) < - 30
226222 val i = block.let { if (it) 30 else 0 } + doNotPlace.let { if (it) - 40 else 0 }
227223 return @NamedScorer i.toDouble()
228224 }
229225 // Trap scorer: Extra points for creating traps -- if you force opponent to play somewhere that will give you the win
230226 // Field importance scorer: If you can create a win condition that will cause a trap (field is important for you)
231227 // Trigger Trap scorer: Extra points if you only need 1 piece to win on BOTH of the levels above point.
232- val nextLevelReveal = NamedScorer <TTT3DPiece , TTT3DPoint >(" nextReveal" ) { piece , point ->
228+ val nextLevelReveal = NamedScorer <TTT3D , TTT3DPoint >(" nextReveal" ) { params , point ->
233229 if (point.z == RANGE .endInclusive) {
234230 return @NamedScorer 0.0
235231 }
236- game. winConditions.filter { it.contains(game .pieces[point.y][point.x][point.z + 1 ]) }
232+ params.param. winConditions.filter { it.contains(params.param .pieces[point.y][point.x][point.z + 1 ]) }
237233 .filter { ! it.contains(point) }
238- .filter { it.canWin(piece .param.opponent()) }.map { it.emptySpaces() }.map {
234+ .filter { it.canWin(params .param.currentPlayer .opponent()) }.map { it.emptySpaces() }.map {
239235 when (it) {
240236 1 -> - 50.0
241237 2 -> - 1.0
@@ -245,14 +241,14 @@ class TTT3DIO(private val game: TTT3D) {
245241 }
246242
247243
248- fun print () {
249- print { it.piece?.toString() ? : " " }
244+ fun print (game : TTT3D ) {
245+ print (game) { it.piece?.toString() ? : " " }
250246 val xwins = game.winConditions.filter { it.canWin(TTT3DPiece .X ) }.groupBy { it.emptySpaces() }.mapValues { it.value.size }
251247 val owins = game.winConditions.filter { it.canWin(TTT3DPiece .O ) }.groupBy { it.emptySpaces() }.mapValues { it.value.size }
252248 println (" Winnables: X $xwins (${xwins.values.sum()} ). O $owins (${owins.values.sum()} )" )
253249 }
254250
255- fun print (function : (TTT3DPoint ) -> String ) {
251+ fun print (game : TTT3D , function : (TTT3DPoint ) -> String ) {
256252 val rowSeperator = " -" .repeat((RANGE .last + 1 ) * (RANGE .last + 4 ) + 1 )
257253 println (rowSeperator)
258254 game.pieces.forEach { y ->
@@ -275,13 +271,13 @@ class TTT3DIO(private val game: TTT3D) {
275271 .withScorer(scorerSabotage)
276272 .withScorer(nextLevelReveal)
277273
278- fun printScores (factory : ScorersConfig <TTT3DPiece , TTT3DPoint >) {
274+ fun printScores (game : TTT3D , factory : ScorersConfig <TTT3D , TTT3DPoint >) {
279275 val scoreDetailsToString: (Pair <String , Double >) -> String = { score ->
280276 val format = " %.4f"
281277 " ${score.first} : ${format.format(score.second)} "
282278 }
283279
284- val scores = factory.producer(game.currentPlayer ).score()
280+ val scores = factory.producer(game).score()
285281 val maxLength: Int = scores.fieldScores.values.flatMap {
286282 it.scores.map { score -> scoreDetailsToString(score.key to score.value).length + 2 }
287283 }.max() ? : 0
@@ -312,26 +308,26 @@ class TTT3DIO(private val game: TTT3D) {
312308 println ()
313309 }
314310
315- fun playVsAI () {
311+ fun playVsAI (game : TTT3D ) {
316312 val scanner = Scanner (System .`in `)
317313
318- while (game.findWinner() == null && scorers.fieldsToScore(game.currentPlayer).toList().isNotEmpty ()) {
314+ while (! game.isGameOver ()) {
319315 if (game.currentPlayer == TTT3DPiece .X ) {
320- makeMove(requestInput(scanner))
316+ makeMove(game, requestInput(game, scanner))
321317// makeMove(alphaBetaPlay(game, 5))
322318// makeMove(aiPlay())
323319 } else {
324- makeMove(alphaBetaPlay(game, 5 ))
320+ makeMove(game, alphaBetaPlay(game, 5 ))
325321// makeMove(aiPlay())
326322 }
327- this .print ()
323+ this .print (game )
328324// Thread.sleep(2000)
329325 }
330- print ()
326+ print (game )
331327 println (game.findWinner())
332328 }
333329
334- private fun makeMove (move : Pair <Int , Int >) {
330+ private fun makeMove (game : TTT3D , move : Pair <Int , Int >) {
335331 val x = move.first
336332 val y = move.second
337333 println (" ${game.currentPlayer} moves at $x , $y " )
@@ -406,10 +402,10 @@ class TTT3DIO(private val game: TTT3D) {
406402 return move.first
407403 }
408404
409- fun aiPlay (): Pair <Int , Int > {
410- this .printScores(this .factory)
405+ fun aiPlay (game : TTT3D ): Pair <Int , Int > {
406+ this .printScores(game, this .factory)
411407
412- val ai = factory.producer(game.currentPlayer )
408+ val ai = factory.producer(game)
413409 val scores = ai.score()
414410 val best = scores.best()
415411 if (best.isEmpty()) {
@@ -419,22 +415,11 @@ class TTT3DIO(private val game: TTT3D) {
419415 return randomBestField.x to randomBestField.y
420416 }
421417
422- fun play () {
423- this .print { point -> game.winConditions.count { it.pieces.contains(point) }.toString() }
424-
425- val scanner = Scanner (System .`in `)
426- while (game.findWinner() == null ) {
427- this .requestInput(scanner)
428- }
429- println (" Winner is ${game.findWinner()} " )
430- this .print ()
431- }
432-
433- private fun requestInput (scanner : Scanner ): Pair <Int , Int > {
418+ private fun requestInput (game : TTT3D , scanner : Scanner ): Pair <Int , Int > {
434419 val alertConfig = scorers.config().withScorer(this .alerts)
435- this .printScores(alertConfig)
420+ this .printScores(game, alertConfig)
436421 println ()
437- this .print ()
422+ this .print (game )
438423 println (" Where do you want to move? Current Player ${game.currentPlayer} " )
439424 val x = scanner.nextInt()
440425 val y = scanner.nextInt()
@@ -443,33 +428,43 @@ class TTT3DIO(private val game: TTT3D) {
443428
444429 data class MoveData (val floorsCount : List <Int >)
445430 data class GameData (val ais : List <Any >, val winner : Any )
446- /*
431+ data class NamedAI (val name : String , val ai : TTT3DAI ) {
432+ override fun toString (): String {
433+ return name
434+ }
435+ }
436+
447437 fun fight () {
448- Fights()
449- .between(scorerAI(factory), alphaBetaAI(3), alphaBetaAI(4),
438+ val fightResult = Fights ()
439+ .between(NamedAI ( " Scorer " , scorerAI(factory) ), alphaBetaAI(3 ), alphaBetaAI(4 ),
450440 alphaBetaAI(5 ), alphaBetaAI(6 ))
451- .fight { players ->
441+ .fight { fight ->
452442 val game = TTT3D ()
453443 while (! game.isGameOver()) {
454- val move = players[game.currentPlayer.playerIndex].play(game)
455- game.playAt(move)
456- this.save(move)
457- this.save(game)
444+ val move = fight.players[game.currentPlayer.playerIndex].ai.invoke(game)
445+ if (! game.playAt(move.second, move.first)) {
446+ print (game)
447+ throw IllegalArgumentException (" Illegal move: $move " )
448+ }
458449 }
459- this.finish(game)
450+ println (" Players ${fight.players} result: ${game.findWinner()} " )
451+ fight.gameResult(fight.players[0 ], WinResult .result(game.isDraw(), game.findWinner() == TTT3DPiece .X ))
452+ fight.gameResult(fight.players[1 ], WinResult .result(game.isDraw(), game.findWinner() == TTT3DPiece .O ))
460453 }
461- .fightEvenly(100)
454+ .fightEvenly(5 )
455+
456+ fightResult.print ()
462457// .index("player")
463458// .dataFinish(moveCount)
464459 }
465- */
466- private fun alphaBetaAI (depth : Int ): TTT3DAI {
467- return { alphaBetaPlay(it, depth) }
460+
461+ private fun alphaBetaAI (depth : Int ): NamedAI {
462+ return NamedAI ( " AlphaBeta $depth " ) { alphaBetaPlay(it, depth) }
468463 }
469464
470- private fun scorerAI (factory : ScorersConfig <TTT3DPiece , TTT3DPoint >): TTT3DAI {
465+ private fun scorerAI (factory : ScorersConfig <TTT3D , TTT3DPoint >): TTT3DAI {
471466 return lambda@{
472- val position = factory.producer(it.currentPlayer ).score().best().random()
467+ val position = factory.producer(it).score().best().random()
473468 return @lambda position.x to position.y
474469 }
475470 }
@@ -502,8 +497,8 @@ net.zomis.spring.games.impls.ur.MonteCarloAI@3590ccd
502497
503498fun main (args : Array <String >) {
504499 val game = loadMap(" XXO | | XO | OX / | OXO | OOOX | / XX | XXO | | OO / OX | | | XX " )
505- TTT3DIO (TTT3D ()).playVsAI()
506-
500+ // TTT3DIO(TTT3D()).playVsAI()
501+ TTT3DIO ().fight()
507502}
508503/*
509504fun game() {
0 commit comments