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:
parent
2f1411f075
commit
c0facfa4d5
@ -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;
|
||||||
|
|||||||
@ -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));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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":
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 },
|
||||||
|
|||||||
@ -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},
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -7,6 +7,5 @@ defmodule Chess.Repo.Migrations.CreateGame do
|
|||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
9
priv/repo/migrations/20180223223018_add_turn_to_game.exs
Normal file
9
priv/repo/migrations/20180223223018_add_turn_to_game.exs
Normal 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
|
||||||
@ -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?
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user