Othello game logic and AI.
https://othello.jumpaku.net/app/
- Start server by executing
docker run -d -p 81:8080 jumpaku/jumpaku-othello
. - jumpaku-othello listens at port
8080
in the container. - Access with
curl localhost:81/v1/api/
and get a responseJumpaku Othello API v1
.
Docker Hub: https://hub.docker.com/r/jumpaku/jumpaku-othello
apt update -y && apt install -y git npm openjdk-8-jdk
- 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
- Run the server by executing
./gradlew run
- The server listens at port
8080
. - Access with
curl localhost:8080/v1/api/
and get a responseJumpaku Othello API v1
.
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.
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]
}
}
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]
}
}
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]
}
}
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 |
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 }
.
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": [] }
.
type Disc = "Dark" | "Light";
type Board = {
darkDiscs: number[],
lightDiscs: number[],
};
darkDiscs
inBoard
represents a list of position indices where dark disc is placed. Each position indexn
indarkDiscs
represents a position (Math.floor(n/8)
,n%8
) on the board.lightDiscs
inBoard
represents a list of position indices where dark disc is placed. Each position indexn
inlightDiscs
represents a position (Math.floor(n/8)
,n%8
) on the board.
type Completed = {
state: "Completed",
darkCount: number,
lightCount: number
};
state: "Completed"
inCompleted
represents that the game has been completed.darkCount
inCompleted
represents the number of dark discs on the board.lightCount
inCompleted
represents the number of light discs on the board.
type InProgress = {
state: "InProgress",
selectPlayer: Disc,
availableMoves: number[]
};
state: "InProgress"
inInProgress
represents that the game is in progress.selectPlayer
inInProgress
represents a color of the disc of player who is making a move.availableMoves
inInProgress
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
inGameStateBase
represents the current board of the game.history
inGameStateBase
represents a list of position indices where the players have been placed.
type GameState = GameStateBase & (Completed | InProgress);
type Error = {
message: string
};
message
inError
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 };
https://jumpaku.hatenablog.com/entry/2019/09/17/Jumpaku_Othello