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%);
|
||||
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 {
|
||||
@ -126,6 +131,11 @@ $board-size: 90vmin;
|
||||
background-color: mix($white-square-color, $selected-square-color, 60%);
|
||||
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) {
|
||||
|
||||
@ -15,10 +15,12 @@ $base-font-size: 16px;
|
||||
$black-square-color: #777;
|
||||
$white-square-color: #bbb;
|
||||
|
||||
$selected-square-color: #6f0;
|
||||
$selected-square-color: #0cf;
|
||||
$available-square-color: #6f0;
|
||||
|
||||
$square-outline-color: darken($black-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";
|
||||
$pieces: king queen bishop knight rook pawn;
|
||||
|
||||
@ -24,10 +24,6 @@ class App extends React.Component {
|
||||
this.channel = new Channel(store, gameId);
|
||||
}
|
||||
|
||||
sendMove(gameId, move) {
|
||||
this.channel.sendMove(move);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { store, gameId } = this.props;
|
||||
|
||||
@ -35,7 +31,7 @@ class App extends React.Component {
|
||||
<ChessBoard
|
||||
gameId={gameId}
|
||||
store={store}
|
||||
sendMove={this.sendMove.bind(this)}
|
||||
channel={this.channel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import classNames from "classnames";
|
||||
|
||||
import API from "../services/api";
|
||||
|
||||
import { setGame, selectPiece } from "../store/actions";
|
||||
import { setGame, setMoves, selectPiece } from "../store/actions";
|
||||
|
||||
class ChessBoardSquare extends React.Component {
|
||||
constructor(props) {
|
||||
@ -16,23 +16,26 @@ class ChessBoardSquare extends React.Component {
|
||||
}
|
||||
|
||||
selectSquare() {
|
||||
const { piece, store, sendMove } = this.props;
|
||||
const { piece, store, channel } = this.props;
|
||||
const { gameId, selectedSquare, player } = store.getState();
|
||||
|
||||
if (selectedSquare != null && this.moveIsValid()) {
|
||||
sendMove(gameId, {
|
||||
store.dispatch(setMoves([]));
|
||||
channel.sendMove({
|
||||
from: selectedSquare,
|
||||
to: this.squareCoords,
|
||||
});
|
||||
} else if (selectedSquare != null) {
|
||||
store.dispatch(setMoves([]));
|
||||
store.dispatch(selectPiece(null));
|
||||
} else if (this.playerCanSelectPiece(player, piece)) {
|
||||
channel.getAvailableMoves(this.squareCoords);
|
||||
store.dispatch(selectPiece(this.squareCoords));
|
||||
}
|
||||
}
|
||||
|
||||
moveIsValid() {
|
||||
return !this.isSelectedSquare();
|
||||
return !this.isSelectedSquare;
|
||||
}
|
||||
|
||||
playerCanSelectPiece(player, piece) {
|
||||
@ -44,7 +47,7 @@ class ChessBoardSquare extends React.Component {
|
||||
player == turn;
|
||||
}
|
||||
|
||||
isSelectedSquare() {
|
||||
get isSelectedSquare() {
|
||||
const { store } = this.props;
|
||||
|
||||
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() {
|
||||
return `f${this.props.file}-r${this.props.rank}`;
|
||||
}
|
||||
|
||||
get squareClass() {
|
||||
if (this.props.piece == undefined) {
|
||||
return "board-square";
|
||||
return classNames(
|
||||
"board-square",
|
||||
{ "available": this.isAvailableSquare }
|
||||
);
|
||||
} else {
|
||||
return classNames(
|
||||
"board-square",
|
||||
this.props.piece.type,
|
||||
this.props.piece.colour,
|
||||
{ "selected": this.isSelectedSquare() }
|
||||
{ "selected": this.isSelectedSquare },
|
||||
{ "available": this.isAvailableSquare }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ class ChessBoard extends React.Component {
|
||||
}
|
||||
|
||||
renderFiles(rankId) {
|
||||
const { store, sendMove } = this.props;
|
||||
const { store, channel } = this.props;
|
||||
const rank = this.getBoard()[rankId];
|
||||
|
||||
return _.map(this.files(rank), fileId => {
|
||||
@ -37,7 +37,7 @@ class ChessBoard extends React.Component {
|
||||
rank={rankId}
|
||||
piece={rank[fileId]}
|
||||
store={store}
|
||||
sendMove={sendMove}
|
||||
channel={channel}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -16,6 +16,11 @@ const chessBoardReducer = (state = defaultState, action) => {
|
||||
.set("selectedSquare", null)
|
||||
.toJS();
|
||||
|
||||
case "SET_MOVES":
|
||||
return Immutable.fromJS(state)
|
||||
.set("moves", action.moves)
|
||||
.toJS();
|
||||
|
||||
case "SET_GAME_ID":
|
||||
return Immutable.fromJS(state)
|
||||
.set("gameId", action.gameId)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import socket from "../socket";
|
||||
import { setPlayer, setGame } from "../store/actions";
|
||||
import { setPlayer, setGame, setMoves } from "../store/actions";
|
||||
|
||||
class Channel {
|
||||
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) {
|
||||
this.channel.push("game:move", move);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const SET_PLAYER = "SET_PLAYER";
|
||||
const SET_GAME = "SET_GAME";
|
||||
const SET_MOVES = "SET_MOVES";
|
||||
const SET_GAME_ID = "SET_GAME_ID";
|
||||
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) => {
|
||||
return {
|
||||
type: SET_GAME_ID,
|
||||
|
||||
@ -4,6 +4,8 @@ const defaultState = {
|
||||
player: null,
|
||||
turn: null,
|
||||
|
||||
moves: [],
|
||||
|
||||
board: {
|
||||
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 },
|
||||
|
||||
@ -20,41 +20,41 @@ defmodule Chess.Board do
|
||||
|
||||
def default do
|
||||
%{
|
||||
"0,7" => %{type: :rook, colour: :black},
|
||||
"1,7" => %{type: :knight, colour: :black},
|
||||
"2,7" => %{type: :bishop, colour: :black},
|
||||
"3,7" => %{type: :queen, colour: :black},
|
||||
"4,7" => %{type: :king, colour: :black},
|
||||
"5,7" => %{type: :bishop, colour: :black},
|
||||
"6,7" => %{type: :knight, colour: :black},
|
||||
"7,7" => %{type: :rook, colour: :black},
|
||||
"0,7" => %{"type" => "rook", "colour" => "black"},
|
||||
"1,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"2,7" => %{"type" => "bishop", "colour" => "black"},
|
||||
"3,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"4,7" => %{"type" => "king", "colour" => "black"},
|
||||
"5,7" => %{"type" => "bishop", "colour" => "black"},
|
||||
"6,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"7,7" => %{"type" => "rook", "colour" => "black"},
|
||||
|
||||
"0,6" => %{type: :pawn, colour: :black},
|
||||
"1,6" => %{type: :pawn, colour: :black},
|
||||
"2,6" => %{type: :pawn, colour: :black},
|
||||
"3,6" => %{type: :pawn, colour: :black},
|
||||
"4,6" => %{type: :pawn, colour: :black},
|
||||
"5,6" => %{type: :pawn, colour: :black},
|
||||
"6,6" => %{type: :pawn, colour: :black},
|
||||
"7,6" => %{type: :pawn, colour: :black},
|
||||
"0,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"1,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"2,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"3,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"4,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"5,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"6,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"7,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
|
||||
"0,1" => %{type: :pawn, colour: :white},
|
||||
"1,1" => %{type: :pawn, colour: :white},
|
||||
"2,1" => %{type: :pawn, colour: :white},
|
||||
"3,1" => %{type: :pawn, colour: :white},
|
||||
"4,1" => %{type: :pawn, colour: :white},
|
||||
"5,1" => %{type: :pawn, colour: :white},
|
||||
"6,1" => %{type: :pawn, colour: :white},
|
||||
"7,1" => %{type: :pawn, colour: :white},
|
||||
"0,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"1,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"2,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"3,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"4,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"5,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"6,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"7,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
|
||||
"0,0" => %{type: :rook, colour: :white},
|
||||
"1,0" => %{type: :knight, colour: :white},
|
||||
"2,0" => %{type: :bishop, colour: :white},
|
||||
"3,0" => %{type: :queen, colour: :white},
|
||||
"4,0" => %{type: :king, colour: :white},
|
||||
"5,0" => %{type: :bishop, colour: :white},
|
||||
"6,0" => %{type: :knight, colour: :white},
|
||||
"7,0" => %{type: :rook, colour: :white}
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"1,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"2,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"5,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"6,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,7 +7,7 @@ defmodule Chess.Moves do
|
||||
piece = board["#{file},#{rank}"]
|
||||
|
||||
case piece do
|
||||
%{type: :pawn} ->
|
||||
%{"type" => "pawn"} ->
|
||||
Pawn.moves(board, {file, rank})
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,12 +5,12 @@ defmodule Chess.Moves.Pawn do
|
||||
piece = board["#{file},#{rank}"]
|
||||
|
||||
case piece do
|
||||
%{colour: :white} ->
|
||||
%{"colour" => "white"} ->
|
||||
case rank do
|
||||
1 -> [{file, rank + 1}, {file, rank + 2}]
|
||||
_ -> [{file, rank + 1}]
|
||||
end
|
||||
%{colour: :black} ->
|
||||
%{"colour" => "black"} ->
|
||||
case rank do
|
||||
6 -> [{file, rank - 1}, {file, rank - 2}]
|
||||
_ -> [{file, rank - 1}]
|
||||
|
||||
@ -5,6 +5,7 @@ defmodule ChessWeb.GameChannel do
|
||||
|
||||
alias Chess.Store.Game
|
||||
alias Chess.Board
|
||||
alias Chess.Moves
|
||||
|
||||
import Chess.Auth, only: [current_user: 1]
|
||||
|
||||
@ -47,10 +48,32 @@ defmodule ChessWeb.GameChannel do
|
||||
{:ok, game} ->
|
||||
send_update(game)
|
||||
|
||||
{:noreply, socket}
|
||||
{:noreply, socket}
|
||||
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
|
||||
payload = %{
|
||||
board: Board.transform(game.board),
|
||||
|
||||
@ -18,7 +18,7 @@ defmodule Chess.Moves.PawnTest do
|
||||
end
|
||||
|
||||
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})
|
||||
|
||||
expected_moves = [{4, 3}]
|
||||
@ -26,7 +26,7 @@ defmodule Chess.Moves.PawnTest do
|
||||
end
|
||||
|
||||
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})
|
||||
|
||||
expected_moves = [{4, 4}]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user