From 976ed2b105e80b257ebb724b3061400f37460041 Mon Sep 17 00:00:00 2001 From: Mike Vink Date: Thu, 24 Jun 2021 23:11:39 +0200 Subject: mega commit --- api/src/main/java/mancala/api/PlayMancala.java | 55 ++++++ api/src/main/java/mancala/api/StartMancala.java | 5 +- api/src/main/java/mancala/api/models/PlayInfo.java | 17 ++ client/src/App.css | 23 ++- client/src/Mancala/Mancala.tsx | 13 +- client/src/Mancala/Pit.css | 46 +++++ client/src/Mancala/Pit.tsx | 65 +++++++ client/src/Mancala/Play.css | 45 ++++- client/src/Mancala/Play.tsx | 207 ++++++++++++++++++++- client/src/Mancala/StartGame.tsx | 45 +++-- client/src/gameState.ts | 8 + client/src/pitState.ts | 0 client/src/pitState.tsx | 0 .../src/main/java/mancala/domain/MancalaImpl.java | 4 +- 14 files changed, 501 insertions(+), 32 deletions(-) create mode 100644 api/src/main/java/mancala/api/PlayMancala.java create mode 100644 api/src/main/java/mancala/api/models/PlayInfo.java create mode 100644 client/src/Mancala/Pit.css create mode 100644 client/src/Mancala/Pit.tsx create mode 100644 client/src/pitState.ts create mode 100644 client/src/pitState.tsx diff --git a/api/src/main/java/mancala/api/PlayMancala.java b/api/src/main/java/mancala/api/PlayMancala.java new file mode 100644 index 0000000..3b59710 --- /dev/null +++ b/api/src/main/java/mancala/api/PlayMancala.java @@ -0,0 +1,55 @@ +package mancala.api; + +import java.io.IOException; +import jakarta.servlet.http.*; +import jakarta.servlet.ServletException; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.*; + +import mancala.api.models.*; +import mancala.domain.MancalaException; +import mancala.domain.MancalaImpl; + +// public class StartMancala { +// @POST +// @Consumes(MediaType.APPLICATION_JSON) +// @Produces(MediaType.APPLICATION_JSON) +// public Response initialize( +// @Context HttpServletRequest request, +// PlayerInput players) { +// // var mancala = new MancalaImpl(); +// var mancala = new MancalaImpl(new int[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13}); +// String namePlayer1 = players.getNameplayer1(); +// String namePlayer2 = players.getNameplayer2(); +// +// HttpSession session = request.getSession(true); +// session.setAttribute("mancala", mancala); +// session.setAttribute("player1", namePlayer1); +// session.setAttribute("player2", namePlayer2); +// +// var output = new Mancala(mancala, namePlayer1, namePlayer2); +// return Response.status(200).entity(output).build(); +// } +// } +@Path("/play") +public class PlayMancala { + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response initialize(@Context HttpServletRequest request, PlayInfo playInfo) { + + HttpSession session = request.getSession(); + + MancalaImpl mancala = (MancalaImpl) session.getAttribute("mancala"); + String namePlayer1 = (String) session.getAttribute("player1"); + String namePlayer2 = (String) session.getAttribute("player2"); + try { + mancala.playPit(playInfo.getIndex()); + } catch (MancalaException e) { + return Response.status(403).build(); + } + var output = new Mancala(mancala, namePlayer1, namePlayer2); + + return Response.status(200).entity(output).build(); + } +} diff --git a/api/src/main/java/mancala/api/StartMancala.java b/api/src/main/java/mancala/api/StartMancala.java index 7bd2330..62339d7 100644 --- a/api/src/main/java/mancala/api/StartMancala.java +++ b/api/src/main/java/mancala/api/StartMancala.java @@ -15,12 +15,13 @@ public class StartMancala { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response initialize( - @Context HttpServletRequest request, + @Context HttpServletRequest request, PlayerInput players) { var mancala = new MancalaImpl(); + // var mancala = new MancalaImpl(new int[] {40,40,40,40,40,40,40,40,40,40,40,40,40,40}); String namePlayer1 = players.getNameplayer1(); String namePlayer2 = players.getNameplayer2(); - + HttpSession session = request.getSession(true); session.setAttribute("mancala", mancala); session.setAttribute("player1", namePlayer1); diff --git a/api/src/main/java/mancala/api/models/PlayInfo.java b/api/src/main/java/mancala/api/models/PlayInfo.java new file mode 100644 index 0000000..9caca4d --- /dev/null +++ b/api/src/main/java/mancala/api/models/PlayInfo.java @@ -0,0 +1,17 @@ +package mancala.api.models; + +public class PlayInfo { + int player; + int index; + + + + public void setPlayer(int player) {this.player = player;} + public int getPlayer() {return player;} + + public void setIndex(int index) { + this.index = index; + } + public int getIndex() {return index;} + +} diff --git a/client/src/App.css b/client/src/App.css index 8eebe3a..1d59297 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1,9 +1,28 @@ +html { + height: 100%; +} + body { margin: 0px; padding: 0px; background-color: #eeeeee; + height: 100%; +} + +#root { + height: 100%; + --board-color: rgb(222,184,135); + --recess-color: rgb(188,143,143); } .main-content { - padding: 8px; -} \ No newline at end of file + display: flex; + + justify-content: center; + align-items: center; + align-self: center; + + flex: 0 1 auto; + + height: calc(100% - (107px)) +} diff --git a/client/src/Mancala/Mancala.tsx b/client/src/Mancala/Mancala.tsx index 1c712c8..509410b 100644 --- a/client/src/Mancala/Mancala.tsx +++ b/client/src/Mancala/Mancala.tsx @@ -9,7 +9,7 @@ import "./Mancala.css"; * users to enter two player names and start a new game. * If there's an active game this component holds the game state. This game state can be passed as a prop * to child components as needed. - * + * * Child components can modify the game state by calling the setGameState (which they recieve as prop.) */ export function Mancala() { @@ -20,9 +20,16 @@ export function Mancala() { // To check if code you added also follows the rules of hooks, run "npm run lint" in the command line const [ gameState, setGameState ] = useState(undefined); + if (localStorage.getItem("state") !== null) { + var state = localStorage.getItem("state"); + const gameState = JSON.parse(state as string); + return + } + if (!gameState) { - return + return } + return -} \ No newline at end of file +} diff --git a/client/src/Mancala/Pit.css b/client/src/Mancala/Pit.css new file mode 100644 index 0000000..8cd3f63 --- /dev/null +++ b/client/src/Mancala/Pit.css @@ -0,0 +1,46 @@ +.Pit { + display: inline-flex; + + justify-content: center; + align-items: center; + + width: 16%; +} + +.Recession { + display: flex; + justify-content: center; + align-items: center; + background: var(--recess-color); +} + +.Pit .Recession { + border-radius: 50%; + height: 50%; + width: 100%; +} + +.Kalaha { + display: inline-flex; + + justify-content: center; + align-items: center; + + width: 10%; +} + +.Kalaha .Recession { + border-radius: 30%; + height: 50%; + width: 100%; +} + +.Stone { + position: absolute; + background: gray; + border-radius: 50%; + border: 2px solid black; + height: 16px; + width: 16px; + min-height: 5px; +} diff --git a/client/src/Mancala/Pit.tsx b/client/src/Mancala/Pit.tsx new file mode 100644 index 0000000..300f3f4 --- /dev/null +++ b/client/src/Mancala/Pit.tsx @@ -0,0 +1,65 @@ +import React, { useState } from "react"; +import type { GameState } from "../gameState"; +import "./Pit.css"; + +type Flatten = T extends any[] ? T[number] : T; + +type Player = Flatten; + +type Pit = Flatten; + +type PitProps = { + player: Player; + index: number; + pit: Pit; + onClick?: (index: number, player: Player, pit: Pit) => (event: React.MouseEvent) => Promise + displayStones: (pit: Pit, spread: "small" | "big") => JSX.Element[] +} + +type SmallBowl = Pit & {kind: "small"}; +type Kalaha = Pit & {kind: "big"}; + + + +export function Pit({player, index, pit, onClick, displayStones}: PitProps) { + const smallBowl = pit as SmallBowl; + smallBowl.kind = "small"; + if (onClick) { + return ( +
+ {pit.nrOfStones > 10 ? "" + pit.nrOfStones : ""} +
+
+ {displayStones(pit, smallBowl.kind)} +
+
+
+ ); + } else { + return ( +
+
+
+ {pit.nrOfStones > 10 ? pit.nrOfStones : ""} + {displayStones(pit, smallBowl.kind)} +
+
+
+ ); + } +} + +export function Kalaha({player, pit, displayStones}: PitProps) { + const kalaha = pit as Kalaha; + kalaha.kind = "big"; + return ( +
+ {pit.nrOfStones > 10 ? pit.nrOfStones : ""} +
+
+ {displayStones(pit, kalaha.kind)} +
+
+
+ ); +} diff --git a/client/src/Mancala/Play.css b/client/src/Mancala/Play.css index 8f73945..6aa0d8f 100644 --- a/client/src/Mancala/Play.css +++ b/client/src/Mancala/Play.css @@ -1 +1,44 @@ -/** Add some styles */ \ No newline at end of file +/** Add some styles */ +#board { + display: flex; + background: var(--board-color); + padding: 20px; + + border-radius: 10%; + + justify-content: center; + flex-flow: row; + flex-direction: row; + + width: 800px; + height: 400px; +} + +#pits { + position: relative; + display: inline-flex; + + + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + + width: 80%; +} + +.playArea { + position: relative; +} + +.playerStatus { + position: relative; + display: flex; + + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + + height: 100px; + +} diff --git a/client/src/Mancala/Play.tsx b/client/src/Mancala/Play.tsx index b72bce1..d2d7f65 100644 --- a/client/src/Mancala/Play.tsx +++ b/client/src/Mancala/Play.tsx @@ -1,17 +1,216 @@ -import React from "react"; +import React, { useState } from "react"; import type { GameState } from "../gameState"; import "./Play.css"; +import { Pit, Kalaha } from "./Pit" + +type Flatten = T extends any[] ? T[number] : T; + +type Player = Flatten; + +type Pit = Flatten; type PlayProps = { gameState: GameState; setGameState(newGameState: GameState): void; } +function xyTranslate(pit: Pit, index: number) { + var angle = index/pit.nrOfStones * 2 * Math.PI; + var d = (index % 2 + 1) * 13; + var ofX = -9; + var ofY = -9; + if (pit.index === 6 || pit.index === 13) { + var xrandom = Math.random(); + var yrandom = Math.random(); + return 'translate('+ ((xrandom > 0.5 ? Math.random() : -Math.random()) * 18 - 10) + 'px, ' + (yrandom > 0.5 ? Math.random() : -Math.random()) * 60 + 'px)'; + } else { + return 'translate('+ (Math.cos(angle) * (d) + ofX) + 'px, ' + (Math.sin(angle) * (d) + ofY) + 'px)'; + } +} + export function Play({ gameState, setGameState }: PlayProps) { + + function playPit(index: number, player: Player, pit: Pit) { + const pitTotal = gameState.players[0].pits.length; + const allPits = gameState.players[0].pits.concat(gameState.players[1].pits); + const stonesToPass = pit.state.stoneElements as JSX.Element[]; + return async function event(event: React.MouseEvent) { + if (!player.hasTurn) return; + console.log("updating server"); + console.log(pit.nrOfStones); + try { + const response = await fetch('mancala/api/play', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({player: player.type === "player1" ? 0 : 1, index: index}) + }); + + if (response.ok) { + const newGameState = await response.json(); + localStorage.setItem("state", JSON.stringify(newGameState)); + setGameState({gameState, ...newGameState}); + } else { + console.error(response.statusText); + } + } catch (error) { + console.error(error.toString()); + } + console.log(pit.nrOfStones); + + } + } + + // function animate(newGameState: GameState) { + // var pits = newGameState.players[0].pits.concat(newGameState.players[1].pits); + // var type; + // for (var pit of pits) { + // type = pit.index === 6 || pit.index === 13 ? "big" : "small" as "big" | "small"; + // displayStones(pit, type); + // setTimeout(() => {}, 1000); + // } + // }; + + function stoneStyler(pit: Pit, index: number, spread: "small" | "big", ) { + const colors = ['gray', 'purple', 'blue', 'green']; + var stoneStyle; + if (spread === "small") { + stoneStyle = { + transform: xyTranslate(pit, index), + background: colors[index % colors.length], + }; + } else { + stoneStyle = { + transform: xyTranslate(pit, index), + background: colors[index % colors.length], + }; + } + return stoneStyle; + } + + function displayStones(pit: Pit, spread: "small" | "big") { + var jsx; + var kind = pit.index === 6 || pit.index === 13 ? "big" : "small" as "small"|"big"; + if (pit.state.stoneElements === undefined) { + pit.state.stoneElements = []; + for (let i = 0; i < pit.nrOfStones; i++) { + jsx = (
); + pit.state.stoneElements.reverse().push(jsx); + } + } else if (pit.nrOfStones < pit.state.stoneElements.length) { + // while (pit.nrOfStones < pit.state.stoneElements.length) { + // pit.state.stoneElements.pop(); + // } + pit.state.stoneElements = []; + for (let i = 0; i < pit.nrOfStones; i++) { + jsx = (
); + pit.state.stoneElements.reverse().push(jsx); + } + } else if (pit.nrOfStones > pit.state.stoneElements.length) { + pit.state.stoneElements = []; + for (let i = 0; i < pit.nrOfStones; i++) { + jsx = (
); + pit.state.stoneElements.reverse().push(jsx); + } + } + return pit.state.stoneElements; + } + + const playerPits = gameState.players.map( + player => { + return player.pits.slice(0, -1).map( + (pit, index) => { + const [ pitState, setPitState ] = useState({stoneElements: undefined}); + pit.state = pitState; + pit.setPitState = setPitState; + return ( + + ) + }); + }); + + const playerKalahas = gameState.players.map( + (player, index) => { + const [ kalahaState, setKalahaState ] = useState({stoneElements: undefined}); + const pit = player.pits[player.pits.length - 1]; + pit.state = kalahaState; + pit.setPitState = setKalahaState; + return ( + + ) + } + ); + + var player2Pits = []; + var player1Pits = []; + + for (var i = 0; i < playerPits[0].length; i++) { + player1Pits.push(playerPits[0][i]); + player2Pits.push(playerPits[1][i]); + } + + const revenge = ( + + ); + return ( -
-

{gameState.players[0].name} vs {gameState.players[1].name}

- To do... +
+
+ {gameState.players[1].name} has {gameState.players[1].pits.reduce((sum, current) => {return (sum + current.nrOfStones)}, 0)} stones. + {gameState.players[1].hasTurn ? " Also has the current turn" : ""} +
+
+ {playerKalahas[1]} +
+ {player2Pits.reverse()} + {player1Pits} +
+ {playerKalahas[0]} +
+
+ {gameState.players[0].name} has {gameState.players[0].pits.reduce((sum, current) => {return (sum + current.nrOfStones)}, 0)} stones. + {gameState.players[0].hasTurn ? "Also has the current turn" : ""} +
+ {gameState.gameStatus.endOfGame ? revenge : ""}
) } diff --git a/client/src/Mancala/StartGame.tsx b/client/src/Mancala/StartGame.tsx index 9d5be2c..4cf8d94 100644 --- a/client/src/Mancala/StartGame.tsx +++ b/client/src/Mancala/StartGame.tsx @@ -1,15 +1,17 @@ import React, { useState } from "react"; import type { GameState } from "../gameState"; import "./StartGame.css"; +import { Play } from "./Play" type StartGameProps = { + gameState: GameState | undefined; setGameState(newGameState: GameState): void; } /** * Allows the players to enter their name. A name is required for both players. They can't have the same names. */ -export function StartGame({ setGameState }: StartGameProps) { +export function StartGame({gameState, setGameState }: StartGameProps) { const [errorMessage, setErrorMessage] = useState(""); const [playerOne, setPlayerOne] = useState(""); @@ -52,23 +54,30 @@ export function StartGame({ setGameState }: StartGameProps) { } } - return ( -
tryStartGame(e)}> - setPlayerOne(e.target.value)} - /> + if (localStorage.getItem("state") !== null) { + var state = localStorage.getItem("state"); + const gameState = JSON.parse(state as string); + setGameState(gameState); + return + } else { + return ( + tryStartGame(e)}> + setPlayerOne(e.target.value)} + /> - setPlayerTwo(e.target.value)} - /> + setPlayerTwo(e.target.value)} + /> -

{errorMessage}

+

{errorMessage}

- - - ) -} \ No newline at end of file + + + ) + } +} diff --git a/client/src/gameState.ts b/client/src/gameState.ts index 2d5ec3e..b276136 100644 --- a/client/src/gameState.ts +++ b/client/src/gameState.ts @@ -16,4 +16,12 @@ interface Player { interface Pit { index: number; nrOfStones: number; + state: PitState; + setPitState: (newPitState: PitState) => void; } + + +interface PitState { + stoneElements: JSX.Element[] | undefined; + } + diff --git a/client/src/pitState.ts b/client/src/pitState.ts new file mode 100644 index 0000000..e69de29 diff --git a/client/src/pitState.tsx b/client/src/pitState.tsx new file mode 100644 index 0000000..e69de29 diff --git a/domain/src/main/java/mancala/domain/MancalaImpl.java b/domain/src/main/java/mancala/domain/MancalaImpl.java index 0090f2d..576eeb4 100644 --- a/domain/src/main/java/mancala/domain/MancalaImpl.java +++ b/domain/src/main/java/mancala/domain/MancalaImpl.java @@ -77,7 +77,7 @@ public class MancalaImpl implements Mancala { return domainReference.toStateArray(new int[15], 0); } - + @Override public int getStonesForPit(int index) { if (MancalaImpl.PLAYER_ONE_PITS.contains(index)) { @@ -114,4 +114,4 @@ public class MancalaImpl implements Mancala { else return Mancala.NO_PLAYERS; } -} \ No newline at end of file +} -- cgit v1.2.3