mirror of
https://github.com/danbee/chess
synced 2025-03-04 08:39:06 +00:00
Show available moves for pawn
This commit is contained in:
parent
c64d40f99a
commit
375be4711c
@ -117,6 +117,11 @@ $board-size: 90vmin;
|
|||||||
background-color: mix($black-square-color, $selected-square-color, 60%);
|
background-color: mix($black-square-color, $selected-square-color, 60%);
|
||||||
box-shadow: 0 0 0 ($board-size / 400) $selected-outline-color;
|
box-shadow: 0 0 0 ($board-size / 400) $selected-outline-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.available {
|
||||||
|
background-color: mix($black-square-color, $available-square-color, 80%);
|
||||||
|
box-shadow: 0 0 0 ($board-size / 400) $available-outline-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin white-square {
|
@mixin white-square {
|
||||||
@ -126,6 +131,11 @@ $board-size: 90vmin;
|
|||||||
background-color: mix($white-square-color, $selected-square-color, 60%);
|
background-color: mix($white-square-color, $selected-square-color, 60%);
|
||||||
box-shadow: 0 0 0 ($board-size / 400) $selected-outline-color;
|
box-shadow: 0 0 0 ($board-size / 400) $selected-outline-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.available {
|
||||||
|
background-color: mix($white-square-color, $available-square-color, 80%);
|
||||||
|
box-shadow: 0 0 0 ($board-size / 400) $available-outline-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-rank:nth-child(odd) {
|
.board-rank:nth-child(odd) {
|
||||||
|
|||||||
@ -15,10 +15,12 @@ $base-font-size: 16px;
|
|||||||
$black-square-color: #777;
|
$black-square-color: #777;
|
||||||
$white-square-color: #bbb;
|
$white-square-color: #bbb;
|
||||||
|
|
||||||
$selected-square-color: #6f0;
|
$selected-square-color: #0cf;
|
||||||
|
$available-square-color: #6f0;
|
||||||
|
|
||||||
$square-outline-color: darken($black-square-color, 20%);
|
$square-outline-color: darken($black-square-color, 20%);
|
||||||
$selected-outline-color: lighten($selected-square-color, 20%);
|
$selected-outline-color: lighten($selected-square-color, 20%);
|
||||||
|
$available-outline-color: rgba(lighten($available-square-color, 20%), 0.7);
|
||||||
|
|
||||||
$colours: "black" "white";
|
$colours: "black" "white";
|
||||||
$pieces: king queen bishop knight rook pawn;
|
$pieces: king queen bishop knight rook pawn;
|
||||||
|
|||||||
@ -24,10 +24,6 @@ class App extends React.Component {
|
|||||||
this.channel = new Channel(store, gameId);
|
this.channel = new Channel(store, gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMove(gameId, move) {
|
|
||||||
this.channel.sendMove(move);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { store, gameId } = this.props;
|
const { store, gameId } = this.props;
|
||||||
|
|
||||||
@ -35,7 +31,7 @@ class App extends React.Component {
|
|||||||
<ChessBoard
|
<ChessBoard
|
||||||
gameId={gameId}
|
gameId={gameId}
|
||||||
store={store}
|
store={store}
|
||||||
sendMove={this.sendMove.bind(this)}
|
channel={this.channel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import classNames from "classnames";
|
|||||||
|
|
||||||
import API from "../services/api";
|
import API from "../services/api";
|
||||||
|
|
||||||
import { setGame, selectPiece } from "../store/actions";
|
import { setGame, setMoves, selectPiece } from "../store/actions";
|
||||||
|
|
||||||
class ChessBoardSquare extends React.Component {
|
class ChessBoardSquare extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -16,23 +16,26 @@ class ChessBoardSquare extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectSquare() {
|
selectSquare() {
|
||||||
const { piece, store, sendMove } = this.props;
|
const { piece, store, channel } = this.props;
|
||||||
const { gameId, selectedSquare, player } = store.getState();
|
const { gameId, selectedSquare, player } = store.getState();
|
||||||
|
|
||||||
if (selectedSquare != null && this.moveIsValid()) {
|
if (selectedSquare != null && this.moveIsValid()) {
|
||||||
sendMove(gameId, {
|
store.dispatch(setMoves([]));
|
||||||
|
channel.sendMove({
|
||||||
from: selectedSquare,
|
from: selectedSquare,
|
||||||
to: this.squareCoords,
|
to: this.squareCoords,
|
||||||
});
|
});
|
||||||
} else if (selectedSquare != null) {
|
} else if (selectedSquare != null) {
|
||||||
|
store.dispatch(setMoves([]));
|
||||||
store.dispatch(selectPiece(null));
|
store.dispatch(selectPiece(null));
|
||||||
} else if (this.playerCanSelectPiece(player, piece)) {
|
} else if (this.playerCanSelectPiece(player, piece)) {
|
||||||
|
channel.getAvailableMoves(this.squareCoords);
|
||||||
store.dispatch(selectPiece(this.squareCoords));
|
store.dispatch(selectPiece(this.squareCoords));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moveIsValid() {
|
moveIsValid() {
|
||||||
return !this.isSelectedSquare();
|
return !this.isSelectedSquare;
|
||||||
}
|
}
|
||||||
|
|
||||||
playerCanSelectPiece(player, piece) {
|
playerCanSelectPiece(player, piece) {
|
||||||
@ -44,7 +47,7 @@ class ChessBoardSquare extends React.Component {
|
|||||||
player == turn;
|
player == turn;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelectedSquare() {
|
get isSelectedSquare() {
|
||||||
const { store } = this.props;
|
const { store } = this.props;
|
||||||
|
|
||||||
if (store.getState().selectedSquare == null) {
|
if (store.getState().selectedSquare == null) {
|
||||||
@ -54,19 +57,32 @@ class ChessBoardSquare extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isAvailableSquare() {
|
||||||
|
const { store } = this.props;
|
||||||
|
const moves = store.getState().moves;
|
||||||
|
|
||||||
|
return _.find(moves, function(square) {
|
||||||
|
return square.join() == this.squareCoords.join();
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
squareId() {
|
squareId() {
|
||||||
return `f${this.props.file}-r${this.props.rank}`;
|
return `f${this.props.file}-r${this.props.rank}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get squareClass() {
|
get squareClass() {
|
||||||
if (this.props.piece == undefined) {
|
if (this.props.piece == undefined) {
|
||||||
return "board-square";
|
return classNames(
|
||||||
|
"board-square",
|
||||||
|
{ "available": this.isAvailableSquare }
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return classNames(
|
return classNames(
|
||||||
"board-square",
|
"board-square",
|
||||||
this.props.piece.type,
|
this.props.piece.type,
|
||||||
this.props.piece.colour,
|
this.props.piece.colour,
|
||||||
{ "selected": this.isSelectedSquare() }
|
{ "selected": this.isSelectedSquare },
|
||||||
|
{ "available": this.isAvailableSquare }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class ChessBoard extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderFiles(rankId) {
|
renderFiles(rankId) {
|
||||||
const { store, sendMove } = this.props;
|
const { store, channel } = this.props;
|
||||||
const rank = this.getBoard()[rankId];
|
const rank = this.getBoard()[rankId];
|
||||||
|
|
||||||
return _.map(this.files(rank), fileId => {
|
return _.map(this.files(rank), fileId => {
|
||||||
@ -37,7 +37,7 @@ class ChessBoard extends React.Component {
|
|||||||
rank={rankId}
|
rank={rankId}
|
||||||
piece={rank[fileId]}
|
piece={rank[fileId]}
|
||||||
store={store}
|
store={store}
|
||||||
sendMove={sendMove}
|
channel={channel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -16,6 +16,11 @@ const chessBoardReducer = (state = defaultState, action) => {
|
|||||||
.set("selectedSquare", null)
|
.set("selectedSquare", null)
|
||||||
.toJS();
|
.toJS();
|
||||||
|
|
||||||
|
case "SET_MOVES":
|
||||||
|
return Immutable.fromJS(state)
|
||||||
|
.set("moves", action.moves)
|
||||||
|
.toJS();
|
||||||
|
|
||||||
case "SET_GAME_ID":
|
case "SET_GAME_ID":
|
||||||
return Immutable.fromJS(state)
|
return Immutable.fromJS(state)
|
||||||
.set("gameId", action.gameId)
|
.set("gameId", action.gameId)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import { setPlayer, setGame } from "../store/actions";
|
import { setPlayer, setGame, setMoves } from "../store/actions";
|
||||||
|
|
||||||
class Channel {
|
class Channel {
|
||||||
constructor(store, gameId) {
|
constructor(store, gameId) {
|
||||||
@ -26,6 +26,13 @@ class Channel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAvailableMoves(square) {
|
||||||
|
this.channel.push("game:get_available_moves", { square })
|
||||||
|
.receive("ok", (data) => {
|
||||||
|
this.store.dispatch(setMoves(data.moves));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sendMove(move) {
|
sendMove(move) {
|
||||||
this.channel.push("game:move", move);
|
this.channel.push("game:move", move);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const SET_PLAYER = "SET_PLAYER";
|
const SET_PLAYER = "SET_PLAYER";
|
||||||
const SET_GAME = "SET_GAME";
|
const SET_GAME = "SET_GAME";
|
||||||
|
const SET_MOVES = "SET_MOVES";
|
||||||
const SET_GAME_ID = "SET_GAME_ID";
|
const SET_GAME_ID = "SET_GAME_ID";
|
||||||
const SELECT_PIECE = "SELECT_PIECE";
|
const SELECT_PIECE = "SELECT_PIECE";
|
||||||
|
|
||||||
@ -18,6 +19,13 @@ export const setGame = (data) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setMoves = (moves) => {
|
||||||
|
return {
|
||||||
|
type: SET_MOVES,
|
||||||
|
moves,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const setGameId = (gameId) => {
|
export const setGameId = (gameId) => {
|
||||||
return {
|
return {
|
||||||
type: SET_GAME_ID,
|
type: SET_GAME_ID,
|
||||||
|
|||||||
@ -4,6 +4,8 @@ const defaultState = {
|
|||||||
player: null,
|
player: null,
|
||||||
turn: null,
|
turn: null,
|
||||||
|
|
||||||
|
moves: [],
|
||||||
|
|
||||||
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 },
|
||||||
7: { a: null, b: null, c: null, d: null, e: null, f: null, g: null, h: null },
|
7: { a: null, b: null, c: null, d: null, e: null, f: null, g: null, h: null },
|
||||||
|
|||||||
@ -20,41 +20,41 @@ defmodule Chess.Board do
|
|||||||
|
|
||||||
def default do
|
def default do
|
||||||
%{
|
%{
|
||||||
"0,7" => %{type: :rook, colour: :black},
|
"0,7" => %{"type" => "rook", "colour" => "black"},
|
||||||
"1,7" => %{type: :knight, colour: :black},
|
"1,7" => %{"type" => "knight", "colour" => "black"},
|
||||||
"2,7" => %{type: :bishop, colour: :black},
|
"2,7" => %{"type" => "bishop", "colour" => "black"},
|
||||||
"3,7" => %{type: :queen, colour: :black},
|
"3,7" => %{"type" => "queen", "colour" => "black"},
|
||||||
"4,7" => %{type: :king, colour: :black},
|
"4,7" => %{"type" => "king", "colour" => "black"},
|
||||||
"5,7" => %{type: :bishop, colour: :black},
|
"5,7" => %{"type" => "bishop", "colour" => "black"},
|
||||||
"6,7" => %{type: :knight, colour: :black},
|
"6,7" => %{"type" => "knight", "colour" => "black"},
|
||||||
"7,7" => %{type: :rook, colour: :black},
|
"7,7" => %{"type" => "rook", "colour" => "black"},
|
||||||
|
|
||||||
"0,6" => %{type: :pawn, colour: :black},
|
"0,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"1,6" => %{type: :pawn, colour: :black},
|
"1,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"2,6" => %{type: :pawn, colour: :black},
|
"2,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"3,6" => %{type: :pawn, colour: :black},
|
"3,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"4,6" => %{type: :pawn, colour: :black},
|
"4,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"5,6" => %{type: :pawn, colour: :black},
|
"5,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"6,6" => %{type: :pawn, colour: :black},
|
"6,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
"7,6" => %{type: :pawn, colour: :black},
|
"7,6" => %{"type" => "pawn", "colour" => "black"},
|
||||||
|
|
||||||
"0,1" => %{type: :pawn, colour: :white},
|
"0,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"1,1" => %{type: :pawn, colour: :white},
|
"1,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"2,1" => %{type: :pawn, colour: :white},
|
"2,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"3,1" => %{type: :pawn, colour: :white},
|
"3,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"4,1" => %{type: :pawn, colour: :white},
|
"4,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"5,1" => %{type: :pawn, colour: :white},
|
"5,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"6,1" => %{type: :pawn, colour: :white},
|
"6,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
"7,1" => %{type: :pawn, colour: :white},
|
"7,1" => %{"type" => "pawn", "colour" => "white"},
|
||||||
|
|
||||||
"0,0" => %{type: :rook, colour: :white},
|
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||||
"1,0" => %{type: :knight, colour: :white},
|
"1,0" => %{"type" => "knight", "colour" => "white"},
|
||||||
"2,0" => %{type: :bishop, colour: :white},
|
"2,0" => %{"type" => "bishop", "colour" => "white"},
|
||||||
"3,0" => %{type: :queen, colour: :white},
|
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||||
"4,0" => %{type: :king, colour: :white},
|
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||||
"5,0" => %{type: :bishop, colour: :white},
|
"5,0" => %{"type" => "bishop", "colour" => "white"},
|
||||||
"6,0" => %{type: :knight, colour: :white},
|
"6,0" => %{"type" => "knight", "colour" => "white"},
|
||||||
"7,0" => %{type: :rook, colour: :white}
|
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,7 +7,7 @@ defmodule Chess.Moves do
|
|||||||
piece = board["#{file},#{rank}"]
|
piece = board["#{file},#{rank}"]
|
||||||
|
|
||||||
case piece do
|
case piece do
|
||||||
%{type: :pawn} ->
|
%{"type" => "pawn"} ->
|
||||||
Pawn.moves(board, {file, rank})
|
Pawn.moves(board, {file, rank})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -5,12 +5,12 @@ defmodule Chess.Moves.Pawn do
|
|||||||
piece = board["#{file},#{rank}"]
|
piece = board["#{file},#{rank}"]
|
||||||
|
|
||||||
case piece do
|
case piece do
|
||||||
%{colour: :white} ->
|
%{"colour" => "white"} ->
|
||||||
case rank do
|
case rank do
|
||||||
1 -> [{file, rank + 1}, {file, rank + 2}]
|
1 -> [{file, rank + 1}, {file, rank + 2}]
|
||||||
_ -> [{file, rank + 1}]
|
_ -> [{file, rank + 1}]
|
||||||
end
|
end
|
||||||
%{colour: :black} ->
|
%{"colour" => "black"} ->
|
||||||
case rank do
|
case rank do
|
||||||
6 -> [{file, rank - 1}, {file, rank - 2}]
|
6 -> [{file, rank - 1}, {file, rank - 2}]
|
||||||
_ -> [{file, rank - 1}]
|
_ -> [{file, rank - 1}]
|
||||||
|
|||||||
@ -5,6 +5,7 @@ defmodule ChessWeb.GameChannel do
|
|||||||
|
|
||||||
alias Chess.Store.Game
|
alias Chess.Store.Game
|
||||||
alias Chess.Board
|
alias Chess.Board
|
||||||
|
alias Chess.Moves
|
||||||
|
|
||||||
import Chess.Auth, only: [current_user: 1]
|
import Chess.Auth, only: [current_user: 1]
|
||||||
|
|
||||||
@ -47,10 +48,32 @@ defmodule ChessWeb.GameChannel do
|
|||||||
{:ok, game} ->
|
{:ok, game} ->
|
||||||
send_update(game)
|
send_update(game)
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_in(
|
||||||
|
"game:get_available_moves",
|
||||||
|
%{"square" => [file, rank]},
|
||||||
|
socket
|
||||||
|
) do
|
||||||
|
game =
|
||||||
|
socket.assigns.current_user_id
|
||||||
|
|> Game.for_user_id()
|
||||||
|
|> Repo.get!(socket.assigns.game_id)
|
||||||
|
|
||||||
|
moves = Moves.available(game.board, {
|
||||||
|
String.to_integer(file),
|
||||||
|
String.to_integer(rank)
|
||||||
|
})
|
||||||
|
|
||||||
|
reply = %{
|
||||||
|
moves: Enum.map(moves, &(Tuple.to_list(&1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
{:reply, {:ok, reply}, socket}
|
||||||
|
end
|
||||||
|
|
||||||
def send_update(game) do
|
def send_update(game) do
|
||||||
payload = %{
|
payload = %{
|
||||||
board: Board.transform(game.board),
|
board: Board.transform(game.board),
|
||||||
|
|||||||
@ -18,7 +18,7 @@ defmodule Chess.Moves.PawnTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "white pawn not on starting square can move forward one space" do
|
test "white pawn not on starting square can move forward one space" do
|
||||||
board = %{"4,2" => %{type: :pawn, colour: :white}}
|
board = %{"4,2" => %{"type" => "pawn", "colour" => "white"}}
|
||||||
moves = Pawn.moves(board, {4, 2})
|
moves = Pawn.moves(board, {4, 2})
|
||||||
|
|
||||||
expected_moves = [{4, 3}]
|
expected_moves = [{4, 3}]
|
||||||
@ -26,7 +26,7 @@ defmodule Chess.Moves.PawnTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "black pawn not on starting square can move forward one space" do
|
test "black pawn not on starting square can move forward one space" do
|
||||||
board = %{"4,5" => %{type: :pawn, colour: :black}}
|
board = %{"4,5" => %{"type" => "pawn", "colour" => "black"}}
|
||||||
moves = Pawn.moves(board, {4, 5})
|
moves = Pawn.moves(board, {4, 5})
|
||||||
|
|
||||||
expected_moves = [{4, 4}]
|
expected_moves = [{4, 4}]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user