Giter VIP home page Giter VIP logo

codingame-scala-kit's Introduction

CodinGame Scala Kit

I love Both CodinGame and Scala because they make programming fun.

CodinGame is great and it would be better if I could

  • Separate codes into packages and files
  • Reuse existing codes
  • Generate the required Player file automatically
  • Avoid tedious inter-IDE copy/paste
  • Unit test the behavior of my Bot
  • Control source code versions with Git
  • Stay in my favorite IDE Intellij

This kit achieves these goals through a source code Bundler which assembles source codes from different packages and files into a single one. Once we remove the constraint imposing us to code in one file, we can organize codes better and make them more reusable.

With continuous building/running feature in SBT, we can generate the fat Player file as soon as code is modified. Thanks to CodinGame Sync, the generated bundle can be synchronized to the online IDE automatically.

Example Bot

The example Bot Ghost in the Cell ranked 59/3509 overall and 2/50 for Scala language.

Setting Up

Pre-requisite

  • Intellij
  • Scala/SBT
  • Git
  • Chrome
  • CodinGame Sync

Step-by-Step Guide

  1. Clone the CodinGame Scala Kit with Git

    git clone https://github.com/truelaurel/CodinGame-Scala-Kit.git

  2. Import the SBT project in Intellij

  3. Open a terminal and fire SBT, hit

    ~test to compile and run unit tests continuously

  4. Open a second terminal and fire SBT, hit

    ~runMain com.truelaurel.codingame.tool.bundle.BundlerMain GhostInTheCell.scala to bundle destination file continuously

  5. Open CodinGame Sync to synchronize Player.scala file continuously to online IDE

CodinGame Scala Kit integration into your own git depo

Using CodinGame Scala Kit as a git submodule inside your own git depot you can both store your solutions into a private depot, and still use and contribute to this scala kit. All instructions are available within this example project

Screenshot

alt tag

FAQ

  1. How Bundler works?

    The Bundler reads a source file. Recursively, it replaces the import statements by source files found in this project.

    By default, the Bundler scans the Player.scala file from src folder and assembles all dependant source codes in a uber Player.scala file under target folder.

  2. Can I reuse codes from third party?

    No, the Bundler only scans source files included in the project.

  3. Is Java Supported?

    No, the Bundler only inlines imported source codes and doesn't adapt Java code to Scala. If you prefer Java, I strongly recommend Manwe's great Competitive Programming tools.

More questions?

Let's discuss it in the CodinGame forum!

codingame-scala-kit's People

Contributors

azzzy avatar dacr avatar huiwang avatar scala-steward avatar tyrcho avatar wei-1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

codingame-scala-kit's Issues

Better package organisation

@huiwang Can we decide here the package organisation ?

I think we need to define :

  1. the root, common package for the organisation. Something like scalakit ?
  2. the package for codingame related stuff (bundler, arena, referee ...)
  3. the package for specific ai algorithms (annealing, mcts, alphabeta ...)
  4. maybe a specific package for game samples (gomoku, tictactoe, connect4 ...)

GameLoop might be a very bad idea

class GameLoop[Context, State, Action](
                                        gameIO: GameIO[Context, State, Action],
                                        myPlayer: GameBot[State, Action],
                                        accumulator: GameAccumulator[Context, State, Action],
                                        turns: Int = 200
                                      ) {
  def run(): Unit = {
    val time = System.nanoTime()
    val initContext = gameIO.readContext
    CGLogger.info("GameInit elt: " + (System.nanoTime() - time) / 1000000 + "ms")
    (1 to turns).foldLeft(initContext) {
      case (c, turn) =>
        val state = gameIO.readState(turn, c)
        CGLogger.info(state)
        val time = System.nanoTime()
        val actions = myPlayer.react(state)
        CGLogger.info("GameReact elt: " + (System.nanoTime() - time) / 1000000 + "ms")
        gameIO.writeAction(state, actions)
        accumulator.accumulate(c, state, actions)
    }
  }
}

GameLoop was originally designed as a rule-tme-all control strcture for all kinds of games. Thanks to the strong contract, we can always model a game with IO, State/Action, and Bot layer. This allows us to have well-defined traits.

However, the strong contract becomes a limitation in cases where we have to adapt the game modeling to meet the contract. The adaptation is often ugly. For example, in the wondev contest, in addtion to the static context, we must also include historical information in the state to clear the fog of war. Consequently, the state object aggregates many information of different nature.

Instead, we should drop such kind of control strucutres and let the develop choose one best for the specific game. Take the wondev example again, I have a control strucutre as follows

object Player {
  def main(args: Array[String]): Unit = {
    CGLogger.current = CGLogger.info

    var turn = 0
    val initContext = WondevIO.readContext
    var previousState: WondevState = null
    var previousAction: WondevAction = null
    var previousOppoScope: Set[Set[Pos]] = null
    while (true) {
      val state = WondevIO.readState(turn, initContext)

      val oppoScope = WarFogAnalysis.restrictOppoScope(
                        state, previousState, 
                        previousAction, previousOppoScope)
      val clearedState = WarFogAnalysis.removeFog(state, oppoScope)
      val action = MinimaxPlayer.react(clearedState)
      previousState = state
      previousAction = action
      previousOppoScope = oppoScope

      WondevIO.writeAction(state, action)
      turn += 1
    }
  }
}

This way, I have a state object which is quite consistent(no mixing of different kind of infomration). It is still debuggable and benchmarkable. The complexity on the accumulator is reduced dramatically thanks to some mutable vars. What might please @tyrcho is that we don't have to implement anymore the IO, State, Bot in the retricted way (extending predefined traits).

9 men morris

https://en.wikipedia.org/wiki/Nine_Men%27s_Morris

Implement a game with intermediate complexity to showcase the usage of our algorithms.

9-men is interesting because the rules change during the 3 game phases (placement, movement, finals with 3 stones), it is not deeply studied like chess and it's not trivial to play optimally like in tic-tac-toe

JMH issue with Bundler

> jmh:run -i 10 -wi 10 -f1 -t1 .*Caribbean.*
[info] Updating {file:/Users/hwang/Developer/ai/CodinGameScalaKit/}codingamescalakit...
[info] Resolving jline#jline;2.14.1 ...
[info] Done updating.
[info] Compiling 69 Scala sources to /Users/hwang/Developer/ai/CodinGameScalaKit/target/scala-2.12/classes...
Processing 254 classes from /Users/hwang/Developer/ai/CodinGameScalaKit/target/scala-2.12/classes with "reflection" generator
Writing out Java source to /Users/hwang/Developer/ai/CodinGameScalaKit/target/scala-2.12/src_managed/jmh and resources to /Users/hwang/Developer/ai/CodinGameScalaKit/target/scala-2.12/resource_managed/jmh
Annotation generator had thrown the exception.
java.lang.IncompatibleClassChangeError: com.tyrcho.Player and com.tyrcho.Player$delayedInit$body disagree on InnerClasses attribute
        at java.lang.Class.getDeclaringClass0(Native Method)
        at java.lang.Class.getDeclaringClass(Class.java:1235)
        at java.lang.Class.getEnclosingClass(Class.java:1277)
        at java.lang.Class.getCanonicalName(Class.java:1392)
        at org.openjdk.jmh.generators.reflection.RFClassInfo.getQualifiedName(RFClassInfo.java:67)
        at org.openjdk.jmh.generators.core.BenchmarkGenerator.buildAnnotatedSet(BenchmarkGenerator.java:206)
        at org.openjdk.jmh.generators.core.BenchmarkGenerator.generate(BenchmarkGenerator.java:75)
        at org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator.main(JmhBytecodeGenerator.java:100)
        at pl.project13.scala.sbt.JmhPlugin$.pl$project13$scala$sbt$JmhPlugin$$internalGenerateBenchmarkSourcesAndResources(JmhPlugin.scala:109)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$2.apply(JmhPlugin.scala:92)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$2.apply(JmhPlugin.scala:90)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:235)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:235)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:249)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:245)
        at sbt.Difference.apply(Tracked.scala:224)
        at sbt.Difference.apply(Tracked.scala:206)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:245)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:244)
        at sbt.Difference.apply(Tracked.scala:224)
        at sbt.Difference.apply(Tracked.scala:200)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:244)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:242)
        at pl.project13.scala.sbt.JmhPlugin$.pl$project13$scala$sbt$JmhPlugin$$generateBenchmarkSourcesAndResources(JmhPlugin.scala:94)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$projectSettings$13.apply(JmhPlugin.scala:50)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$projectSettings$13.apply(JmhPlugin.scala:50)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
        at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
        at sbt.std.Transform$$anon$4.work(System.scala:63)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
        at sbt.Execute.work(Execute.scala:235)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
        at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.