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 classNames from "classnames";
import { selectPiece, setBoard } from "../store/actions";
import { selectPiece, setGame } from "../store/actions";
class ChessBoardSquare extends React.Component {
constructor(props) {
@ -16,19 +16,35 @@ class ChessBoardSquare extends React.Component {
selectSquare() {
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({
method: "PATCH",
url: "/api/games/" + gameId,
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()));
}
};
}
moveIsValid() {
return !this.isSelectedSquare();
}
playerCanSelectPiece(player, piece) {
var { store } = this.props;
var { turn } = store.getState();
return piece != undefined &&
piece.colour == player &&
player == turn;
}
isSelectedSquare() {
var { store } = this.props;

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
const defaultState = {
selectedSquare: null,
player: "white",
turn: "white",
player: null,
turn: null,
board: {
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
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
%{
"0,7" => %{type: :rook, colour: :black},

View File

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

View File

@ -25,20 +25,24 @@ defmodule ChessWeb.Api.GameController do
|> Repo.get!(id)
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
{:ok, game} ->
conn
|> json(Board.transform(game.board))
|> json(game_attrs(conn, game))
end
end
defp game_attrs(conn, game) do
%{
board: Board.transform(game.board),
player: player(conn, game)
player: player(conn, game),
turn: game.turn
}
end
@ -49,13 +53,4 @@ defmodule ChessWeb.Api.GameController do
"black"
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

View File

@ -7,6 +7,5 @@ defmodule Chess.Repo.Migrations.CreateGame do
timestamps()
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")
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)
assert changeset.valid?
@ -19,7 +24,12 @@ defmodule Chess.GameTest do
end
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)
assert changeset.valid?