Current section
Files
Jump to
Current section
Files
src/game_server.gleam
import color.{type Color}
import game.{type Game}
import gleam/erlang/process.{type Subject}
import gleam/option.{type Option}
import gleam/otp/actor
import gleam/result
import move.{type Move}
import status.{type Status}
pub type Message {
AllLegalMoves(reply_with: Subject(List(Move)))
ApplyMove(reply_with: Subject(Result(Game, String)), move: Move)
ApplyMoveUciString(reply_with: Subject(Result(Game, String)), move: String)
ApplyMoveSanString(reply_with: Subject(Result(Game, String)), move: String)
ApplyMoveRaw(reply_with: Subject(Result(Game, String)), move: Move)
UndoMove(reply_with: Subject(Result(Game, String)))
GetState(reply_with: Subject(Game))
GetSideToMove(reply_with: Subject(Color))
GetFen(reply_with: Subject(String))
GetStatus(reply_with: Subject(Option(Status)))
NewGame(reply_with: Subject(Game))
NewGameFromFen(reply_with: Subject(Result(Game, String)), fen: String)
DisableStatus(reply_with: Subject(Result(Game, Nil)))
Shutdown
PrintBoard(reply_with: Subject(Result(String, String)))
}
// TODO: This module contains all functions related to interacting
// with erlang processes. It keeps the library from being compiled
// to javascript. At some point in the future, I would like to
// address this issue so that the library can target js.
pub fn print_board(game_actor: Subject(Message)) {
process.call(game_actor, PrintBoard, 1000)
}
pub fn apply_move(game_actor: Subject(Message), move: Move) {
process.call(game_actor, ApplyMove(_, move), 1000)
}
pub fn apply_move_uci_string(
game_actor: Subject(Message),
move_uci: String,
) -> Result(Game, _) {
process.call(game_actor, ApplyMoveUciString(_, move_uci), 1000)
}
pub fn apply_move_san_string(game_actor: Subject(Message), move_san: String) {
process.call(game_actor, ApplyMoveSanString(_, move_san), 1000)
}
pub fn apply_move_raw(game_actor: Subject(Message), move: Move) {
process.call(game_actor, ApplyMoveRaw(_, move), 1000)
}
pub fn undo_move(game_actor: Subject(Message)) {
process.call(game_actor, UndoMove, 1000)
}
pub fn all_legal_moves(game_actor: Subject(Message)) {
process.call(game_actor, AllLegalMoves, 1000)
}
pub fn get_fen(game_actor: Subject(Message)) {
process.call(game_actor, GetFen, 1000)
}
pub fn get_status(game_actor: Subject(Message)) {
process.call(game_actor, GetStatus, 1000)
}
pub fn disable_status(game_actor: Subject(Message)) {
process.call(game_actor, DisableStatus, 1000)
}
pub fn new_game(game_actor: Subject(Message)) {
process.call(game_actor, NewGame, 1000)
}
pub fn new_game_from_fen(game_actor: Subject(Message), fen: String) {
process.call(game_actor, NewGameFromFen(_, fen), 1000)
}
pub fn shutdown(game_actor: Subject(Message)) {
process.send(game_actor, Shutdown)
}
fn handle_message(message: Message, game: Game) -> actor.Next(Message, Game) {
case message {
AllLegalMoves(client) ->
case handle_all_legal_moves(game, client) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
ApplyMove(client, move) ->
case handle_apply_move(game, client, move) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
ApplyMoveUciString(client, move) ->
case handle_apply_move_uci(game, client, move) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
ApplyMoveSanString(client, move) ->
case handle_apply_move_san_string(game, client, move) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
ApplyMoveRaw(client, move) ->
case handle_apply_move_raw(game, client, move) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
UndoMove(client) ->
case handle_undo_move(game, client) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
GetState(client) -> {
process.send(client, game)
actor.continue(game)
}
GetFen(client) ->
case handle_get_fen(game, client) {
Ok(next) -> next
Error(_) -> actor.continue(game)
}
GetStatus(client) -> {
process.send(client, game.status)
actor.continue(game)
}
GetSideToMove(client) -> {
process.send(client, game.turn)
actor.continue(game)
}
NewGame(client) -> {
let new_game = game.new_game()
process.send(client, new_game)
actor.continue(new_game)
}
NewGameFromFen(client, fen) -> {
case game.from_fen_string(fen) {
Ok(new_game) -> {
process.send(client, Ok(new_game))
actor.continue(new_game)
}
Error(_) -> {
process.send(client, Error("Failed to create game from fen"))
actor.continue(game)
}
}
}
DisableStatus(client) -> {
let new_game = game.disable_status(game)
process.send(client, Ok(new_game))
actor.continue(new_game)
}
Shutdown -> actor.Stop(process.Normal)
PrintBoard(client) -> handle_print_board(game, client)
}
}
fn handle_all_legal_moves(
game: Game,
client: Subject(List(Move)),
) -> Result(actor.Next(Message, Game), _) {
use all_legal_moves <- result.try(game.all_legal_moves(game))
process.send(client, all_legal_moves)
Ok(actor.continue(game))
}
fn handle_undo_move(
game: Game,
client: Subject(Result(Game, _)),
) -> Result(actor.Next(Message, Game), _) {
case game.undo_move(game) {
Ok(new_game_state) -> {
process.send(client, Ok(new_game_state))
Ok(actor.continue(new_game_state))
}
Error(_) -> {
process.send(client, Error("Failed to undo move"))
Ok(actor.continue(game))
}
}
}
fn handle_apply_move_san_string(
game: Game,
client: Subject(Result(Game, _)),
move: String,
) -> Result(actor.Next(Message, Game), _) {
case game.apply_move_san_string(game, move) {
Ok(new_game_state) -> {
process.send(client, Ok(new_game_state))
Ok(actor.continue(new_game_state))
}
Error(_) -> {
process.send(client, Error("Failed to apply move"))
Ok(actor.continue(game))
}
}
}
fn handle_apply_move_uci(
game: Game,
client: Subject(Result(Game, _)),
move: String,
) -> Result(actor.Next(Message, Game), _) {
case game.apply_move_uci(game, move) {
Ok(new_game_state) -> {
process.send(client, Ok(new_game_state))
Ok(actor.continue(new_game_state))
}
Error(_) -> {
process.send(client, Error("Failed to apply move"))
Ok(actor.continue(game))
}
}
}
fn handle_apply_move(
game: Game,
client: Subject(Result(Game, _)),
move: Move,
) -> Result(actor.Next(Message, Game), _) {
case game.apply_move(game, move) {
Ok(new_game_state) -> {
process.send(client, Ok(new_game_state))
Ok(actor.continue(new_game_state))
}
Error(_) -> {
process.send(client, Error("Failed to apply move"))
Ok(actor.continue(game))
}
}
}
fn handle_apply_move_raw(
game: Game,
client: Subject(Result(Game, _)),
move: Move,
) -> Result(actor.Next(Message, Game), _) {
let new_game_state = game.apply_move_raw(game, move)
process.send(client, new_game_state)
Ok(case new_game_state {
Ok(new_game_state) -> actor.continue(new_game_state)
Error(_) -> actor.continue(game)
})
}
fn handle_get_fen(game: Game, client: Subject(String)) {
process.send(client, game.to_fen(game))
Ok(actor.continue(game))
}
fn handle_print_board(
game: Game,
client: Subject(Result(String, String)),
) -> actor.Next(Message, Game) {
process.send(client, game.print_board(game))
actor.continue(game)
}
pub fn new_server() {
actor.start(game.new_game(), handle_message)
}
pub fn load_pgn(pgn_string: String) {
case game.load_pgn(pgn_string) {
Ok(game) -> {
case actor.start(game, handle_message) {
Ok(actor) -> Ok(actor)
Error(_) -> Error("Failed to start actor")
}
}
Error(error) -> Error(error)
}
}