Skip to content

Latest commit

 

History

History
281 lines (211 loc) · 6.5 KB

README.md

File metadata and controls

281 lines (211 loc) · 6.5 KB

JumpakuOthello

Othello game logic and AI.

Play with example web application

https://othello.jumpaku.net/app/

Install with Docker

  1. Start server by executing docker run -d -p 81:8080 jumpaku/jumpaku-othello.
  2. jumpaku-othello listens at port 8080 in the container.
  3. Access with curl localhost:81/v1/api/ and get a response Jumpaku Othello API v1.

Docker Hub: https://hub.docker.com/r/jumpaku/jumpaku-othello

Install without Docker

Prerequisite

apt update -y && apt install -y git npm openjdk-8-jdk

Installation

  1. Build web resources and server by executing
WORKDIR=$(pwd)
# Clone repository
git clone https://github.com/Jumpaku/JumpakuOthello.git
# Build web resources
cd "${WORKDIR}"/JumpakuOthello/web 
npm install && npm run build
cp -r "${WORKDIR}"/JumpakuOthello/web/dist "${WORKDIR}"/JumpakuOthello/api/src/main/resources/
# Build server
cd "${WORKDIR}"/JumpakuOthello/api/
./gradlew build
  1. Run the server by executing
./gradlew run
  1. The server listens at port 8080.
  2. Access with curl localhost:8080/v1/api/ and get a response Jumpaku Othello API v1.

API specification

Handling games

URI Path Method Request body type Response body type
Make a new game origin/v1/games/?action=make POST (None) { gameId: string, gameState: GameStateResult }
Get a state of the game origin/v1/games/?action=get POST { gameId: string } { gameId: string, gameState: GameStateResult }
Make a move origin/v1/games/?action=move POST { gameId: string, move: number } { gameId: string, gameState: GameStateResult }

origin is https://othello.jumpaku.net or where you installed jumpaku-othello.

Examples of handling a game

Make new game

Request

curl -X POST https://othello.jumpaku.net/v1/games/?action=make

Response

{
  "gameId": "adddaabc-ccc8-4ebb-b1f4-b8b4e7e9087c",
  "gameState": {
    "board": {
      "darkDiscs": [28,35],
      "lightDiscs": [27,36]
    },
    "history": [],
    "state": "InProgress",
    "selectPlayer": "Dark",
    "availableMoves": [19,26,37,44]
  }
}

Get current game state

Request

curl -X POST https://othello.jumpaku.net/v1/games/?action=get -d '{ "gameId": "adddaabc-ccc8-4ebb-b1f4-b8b4e7e9087c" }'

Response

{
  "gameId": "adddaabc-ccc8-4ebb-b1f4-b8b4e7e9087c",
  "gameState": {
    "board": {
      "darkDiscs": [28,35],
      "lightDiscs": [27,36]
    },
    "history": [],
    "state": "InProgress",
    "selectPlayer": "Dark",
    "availableMoves": [19,26,37,44]
  }
}

Make a move

Request

curl -X POST https://othello.jumpaku.net/v1/games/?action=move -d '{ "gameId": "adddaabc-ccc8-4ebb-b1f4-b8b4e7e9087c", "move": 19 }'

Response

{
  "gameId": "adddaabc-ccc8-4ebb-b1f4-b8b4e7e9087c",
  "gameState": {
    "board": {
      "darkDiscs": [19,27,28,35],
      "lightDiscs": [36]
    },
    "history": [19],
    "state": "InProgress",
    "selectPlayer": "Light",
    "availableMoves": [18,20,34]
  }
}

Inquiring to the AI

URI Path Method Request body type Response body type
Get a move from AI origin/v1/ai/move POST { selectPlayer: Disc, board: Board } MoveResult
Evaluate available moves by AI origin/v1/ai/moves POST { selectPlayer: Disc, board: Board } MovesResult

Example of getting a move from AI

Request

curl -X POST https://othello.jumpaku.net/v1/ai/move -d '{ "board": { "darkDiscs": [19,27,28,35], "lightDiscs": [36] }, "selectPlayer": "Light" }'

Response

{
  "move": 18
}

If there is no position to place disc, you receive { "move": -1 }.

Example of evaluating available moves by AI

Request

curl -X POST https://othello.jumpaku.net/v1/ai/moves -d '{ "board": { "darkDiscs": [19,27,28,35], "lightDiscs": [36] }, "selectPlayer": "Light" }' 

Response

{
  "moves": [
    {
      "move": 18, "evaluation": -20
    }, {
      "move": 20, "evaluation": -8.4
    }, {
      "move": 34, "evaluation": -1.6
    }
  ]
}

Elements in moves are not sorted. If there is no position to place disc, you receive { "moves": [] }.

Definition of Types

type Disc = "Dark" | "Light";
type Board = {
    darkDiscs: number[],
    lightDiscs: number[],
};
  • darkDiscs in Board represents a list of position indices where dark disc is placed. Each position index n in darkDiscs represents a position (Math.floor(n/8), n%8) on the board.
  • lightDiscs in Board represents a list of position indices where dark disc is placed. Each position index n in lightDiscs represents a position (Math.floor(n/8), n%8) on the board.
type Completed = {
    state: "Completed",
    darkCount: number,
    lightCount: number
};
  • state: "Completed" in Completed represents that the game has been completed.
  • darkCount in Completed represents the number of dark discs on the board.
  • lightCount in Completed represents the number of light discs on the board.
type InProgress = {
    state: "InProgress",
    selectPlayer: Disc,
    availableMoves: number[]
};
  • state: "InProgress" in InProgress represents that the game is in progress.
  • selectPlayer in InProgress represents a color of the disc of player who is making a move.
  • availableMoves in InProgress represents a list of position indices where the current player can select. If there is no position to place a disc, availableMoves is [-1].
type GameStateBase = {
    board: Board,
    history: number[],
};
  • board in GameStateBase represents the current board of the game.
  • history in GameStateBase represents a list of position indices where the players have been placed.
type GameState = GameStateBase & (Completed | InProgress);
type Error = {
    message: string
};
  • message in Error represents an error message from the API server.
type GameStateResult = {
  gameId: string,
  gameState: GameState
} | Error;
  • gameId identifies which game you want to handle.
type MoveResult = { move: number } | Error;
type MovesResult = { moves: EvaluatedMove[] } | Error;
type EvaluatedMove = { move: number: evaluation: number };

More information

https://jumpaku.hatenablog.com/entry/2019/09/17/Jumpaku_Othello