1
0
mirror of https://github.com/danbee/chess synced 2025-03-04 08:39:06 +00:00

Players take turns

Restricts players to only be able to move their own pieces and only when
it's their turn.
This commit is contained in:
Daniel Barber 2018-02-24 15:52:22 -05:00
parent 2f1411f075
commit c0facfa4d5
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
11 changed files with 87 additions and 39 deletions

View File

@ -3,7 +3,7 @@ import _ from "lodash";
import $ from "jquery"; import $ from "jquery";
import classNames from "classnames"; import classNames from "classnames";
import { selectPiece, setBoard } from "../store/actions"; import { selectPiece, setGame } from "../store/actions";
class ChessBoardSquare extends React.Component { class ChessBoardSquare extends React.Component {
constructor(props) { constructor(props) {
@ -16,19 +16,35 @@ class ChessBoardSquare extends React.Component {
selectSquare() { selectSquare() {
var { piece, store } = this.props; var { piece, store } = this.props;
var { gameId, selectedSquare } = store.getState(); var { gameId, selectedSquare, player } = store.getState();
if (selectedSquare != null) { if (selectedSquare != null && this.moveIsValid()) {
$.ajax({ $.ajax({
method: "PATCH", method: "PATCH",
url: "/api/games/" + gameId, url: "/api/games/" + gameId,
data: { move: { from: selectedSquare, to: this.squareCoords() } } data: { move: { from: selectedSquare, to: this.squareCoords() } }
}).then((data) => store.dispatch(setBoard(data))); }).then((data) => store.dispatch(setGame(data)));
} }
else if (piece != undefined) { else if (selectedSquare != null) {
store.dispatch(selectPiece(null));
}
else if (this.playerCanSelectPiece(player, piece)) {
store.dispatch(selectPiece(this.squareCoords())); store.dispatch(selectPiece(this.squareCoords()));
} }
}; }
moveIsValid() {
return !this.isSelectedSquare();
}
playerCanSelectPiece(player, piece) {
var { store } = this.props;
var { turn } = store.getState();
return piece != undefined &&
piece.colour == player &&
player == turn;
}
isSelectedSquare() { isSelectedSquare() {
var { store } = this.props; var { store } = this.props;

View File

@ -2,7 +2,7 @@ import React from "react";
import _ from "lodash"; import _ from "lodash";
import $ from "jquery"; import $ from "jquery";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { setBoard, setPlayer, setGameId } from "../store/actions"; import { setGame, setGameId } from "../store/actions";
import ChessBoardSquare from "./chess-board-square"; import ChessBoardSquare from "./chess-board-square";
@ -14,8 +14,7 @@ class ChessBoard extends React.Component {
$.ajax({ method: "GET", url: `/api/games/${gameId}` }) $.ajax({ method: "GET", url: `/api/games/${gameId}` })
.then((data) => { .then((data) => {
store.dispatch(setBoard(data.board)); store.dispatch(setGame(data));
store.dispatch(setPlayer(data.player));
}); });
} }

View File

@ -4,15 +4,12 @@ import defaultState from "../store/default-state";
const chessBoardReducer = (state = defaultState, action) => { const chessBoardReducer = (state = defaultState, action) => {
switch (action.type) { switch (action.type) {
case "SET_BOARD": case "SET_GAME":
return Immutable.fromJS(state) return Immutable.fromJS(state)
.set("board", action.board) .set("board", action.board)
.set("selectedSquare", null)
.toJS();
case "SET_PLAYER":
return Immutable.fromJS(state)
.set("player", action.player) .set("player", action.player)
.set("turn", action.turn)
.set("selectedSquare", null)
.toJS(); .toJS();
case "SET_GAME_ID": case "SET_GAME_ID":

View File

@ -1,12 +1,13 @@
const SET_BOARD = "SET_BOARD"; const SET_GAME = "SET_GAME";
const SET_PLAYER = "SET_PLAYER";
const SET_GAME_ID = "SET_GAME_ID"; const SET_GAME_ID = "SET_GAME_ID";
const SELECT_PIECE = "SELECT_PIECE"; const SELECT_PIECE = "SELECT_PIECE";
export const setBoard = (board) => { export const setGame = (data) => {
return { return {
type: SET_BOARD, type: SET_GAME,
board: board board: data.board,
player: data.player,
turn: data.turn
} }
} }

View File

@ -1,8 +1,8 @@
const defaultState = { const defaultState = {
selectedSquare: null, selectedSquare: null,
player: "white", player: null,
turn: "white", turn: null,
board: { board: {
8: { a: null, b: null, c: null, d: null, e: null, f: null, g: null, h: null }, 8: { a: null, b: null, c: null, d: null, e: null, f: null, g: null, h: null },

View File

@ -9,6 +9,15 @@ defmodule Chess.Board do
end) end)
end end
def move_piece(board, move_params) do
[from_file, from_rank] = move_params["from"]
[to_file, to_rank] = move_params["to"]
{piece, board} = Map.pop(board, "#{from_file},#{from_rank}")
Map.put(board, "#{to_file},#{to_rank}", piece)
end
def default do def default do
%{ %{
"0,7" => %{type: :rook, colour: :black}, "0,7" => %{type: :rook, colour: :black},

View File

@ -12,6 +12,7 @@ defmodule Chess.Store.Game do
schema "games" do schema "games" do
field :board, :map field :board, :map
field :turn, :string
belongs_to :user, Chess.Auth.User belongs_to :user, Chess.Auth.User
belongs_to :opponent, Chess.Auth.User, references: :id belongs_to :opponent, Chess.Auth.User, references: :id
@ -24,7 +25,8 @@ defmodule Chess.Store.Game do
|> cast(params, required_attrs()) |> cast(params, required_attrs())
|> foreign_key_constraint(:user_id) |> foreign_key_constraint(:user_id)
|> foreign_key_constraint(:opponent_id) |> foreign_key_constraint(:opponent_id)
|> put_change(:board, Board.default) |> put_change(:board, Board.default())
|> put_change(:turn, default_turn())
|> validate_required(required_attrs()) |> validate_required(required_attrs())
end end
@ -36,6 +38,13 @@ defmodule Chess.Store.Game do
|> validate_required(required_attrs()) |> validate_required(required_attrs())
end end
def change_turn(turn) do
case turn do
"white" -> "black"
"black" -> "white"
end
end
def for_user(user) do def for_user(user) do
from game in Game, from game in Game,
where: game.user_id == ^user.id, where: game.user_id == ^user.id,
@ -47,5 +56,9 @@ defmodule Chess.Store.Game do
|> order_by([game], desc: game.inserted_at) |> order_by([game], desc: game.inserted_at)
end end
defp required_attrs, do: ~w[board user_id opponent_id]a defp required_attrs, do: ~w[board turn user_id opponent_id]a
defp default_turn do
"white"
end
end end

View File

@ -25,20 +25,24 @@ defmodule ChessWeb.Api.GameController do
|> Repo.get!(id) |> Repo.get!(id)
changeset = Game.changeset( changeset = Game.changeset(
game, %{board: new_board(game.board, move_params)} game, %{
board: Board.move_piece(game.board, move_params),
turn: Game.change_turn(game.turn)
}
) )
case Repo.update(changeset) do case Repo.update(changeset) do
{:ok, game} -> {:ok, game} ->
conn conn
|> json(Board.transform(game.board)) |> json(game_attrs(conn, game))
end end
end end
defp game_attrs(conn, game) do defp game_attrs(conn, game) do
%{ %{
board: Board.transform(game.board), board: Board.transform(game.board),
player: player(conn, game) player: player(conn, game),
turn: game.turn
} }
end end
@ -49,13 +53,4 @@ defmodule ChessWeb.Api.GameController do
"black" "black"
end end
end end
defp new_board(board, move_params) do
[from_file, from_rank] = move_params["from"]
[to_file, to_rank] = move_params["to"]
{piece, board} = Map.pop(board, "#{from_file},#{from_rank}")
Map.put(board, "#{to_file},#{to_rank}", piece)
end
end end

View File

@ -7,6 +7,5 @@ defmodule Chess.Repo.Migrations.CreateGame do
timestamps() timestamps()
end end
end end
end end

View File

@ -0,0 +1,9 @@
defmodule Chess.Repo.Migrations.AddTurnToGame do
use Ecto.Migration
def change do
alter table("games") do
add :turn, :string, default: "white"
end
end
end

View File

@ -11,7 +11,12 @@ defmodule Chess.GameTest do
user = create_user("link", "ilovezelda") user = create_user("link", "ilovezelda")
opponent = create_user("zelda", "ganonsucks") opponent = create_user("zelda", "ganonsucks")
attrs = %{board: %{}, user_id: user.id, opponent_id: opponent.id} attrs = %{
board: %{},
user_id: user.id,
opponent_id: opponent.id,
turn: "white"
}
changeset = Game.changeset(%Game{}, attrs) changeset = Game.changeset(%Game{}, attrs)
assert changeset.valid? assert changeset.valid?
@ -19,7 +24,12 @@ defmodule Chess.GameTest do
end end
test "game cannot be saved if the user or opponent do not exist" do test "game cannot be saved if the user or opponent do not exist" do
attrs = %{board: %{}, user_id: 1, opponent_id: 2} attrs = %{
board: %{},
user_id: 1,
opponent_id: 2,
turn: "white"
}
changeset = Game.changeset(%Game{}, attrs) changeset = Game.changeset(%Game{}, attrs)
assert changeset.valid? assert changeset.valid?