Skip to content

Commit

Permalink
Merge pull request #25 from pishposh/variable-cards-in-play
Browse files Browse the repository at this point in the history
Variable cards in play
  • Loading branch information
HighPharoah authored Nov 7, 2024
2 parents 63b479d + 6d579d3 commit b29e710
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 14 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/sign-content/moo-deng.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/sign-content/scabby.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/sign-content/tick-tock-avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/sign-content/union-fist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import './App.css';
import { Header } from './components/Header';
import { InfoDialog } from './components/InfoDialog';
import { PicketSign } from './components/PicketSign';
import { GameSettings } from './components/GameSettings';
import { ResultsDialog } from './components/ResultsDialog';
import { Scoreboard } from './components/Scoreboard';
import { Game, NewGame } from './game';

function App() {
const [game, setGame] = useState<Game>(NewGame());
const [duration, setDuration] = useState('0m 0s');
const [gameSettingsOpen, setGameSettingsOpen] = useState(false);
const [showDialog, setShowDialog] = useState(false);
const [infoDialogOpen, setInfoDialogOpen] = useState(false);

Expand Down Expand Up @@ -58,6 +60,22 @@ function App() {
>
What’s this?
</span>

{infoDialogOpen && (
<InfoDialog onClose={() => setInfoDialogOpen(false)} />
)}
<a style={{cursor: "pointer"}} onClick={() => setGameSettingsOpen(!gameSettingsOpen)}>
Settings
</a>
{gameSettingsOpen && (
<GameSettings
onClose={() => setGameSettingsOpen(false)}
onSave={(difficulty) => {
//Reset the game with the new difficulty
setGame(game.resetWithDifficulty(difficulty))
}}
/>
)}
</Header>

<div id="game-container">
Expand Down
21 changes: 15 additions & 6 deletions src/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ enum SignContent {
MakerWeekGuild = 'maker-week-guild.webp',
UnionMadeGithub = 'union-made-github.webp',
WuerkerCartoon = 'wuerker-cartoon.webp',
ContractCrossword = 'contract-crossword.webp'
ContractCrossword = 'contract-crossword.webp',
DontPlayGames = 'dontplaygames-socialstrike.png',
MooDeng = 'moo-deng.png',
Scabby = 'scabby.jpg',
StandWithGuild = 'stand-with-guild-avatar.png',
TechSupport = 'tech-support-avatar.png',
TickTock = 'tick-tock-avatar.png',
UnionFist = 'union-fist.png',
ReadyToStrike = 'ready-to-strike_text-only-avatar.png',
}

export interface Card {
Expand All @@ -17,12 +25,13 @@ export interface Card {
count: number;
}

const CARD_VALUES = Object.values(SignContent);

export function getInitialCards(): Card[] {
export function getInitialCards(countCardsInPlay: number): Card[] {
const date = new Date().toISOString();
let cardValues = Object.values(SignContent);
shuffleArray(cardValues);
cardValues = cardValues.slice(0, countCardsInPlay);

const cards = [...CARD_VALUES, ...CARD_VALUES].map((value, index) => ({
const cards = [...cardValues, ...cardValues].map((value, index) => ({
value,
id: date + index,
isFaceUp: false,
Expand All @@ -34,7 +43,7 @@ export function getInitialCards(): Card[] {
}

// note, sort(() => 0.5 - Math.random()) and similar are biased; see <https://stackoverflow.com/a/12646864>
function shuffleArray(array: Card[]) {
function shuffleArray(array: unknown[]) {
for (let i = array.length - 1; i >= 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
Expand Down
30 changes: 30 additions & 0 deletions src/components/GameSettings/GameSettings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.settings-dialog {
z-index: 100;
overflow-y: scroll;
border: none;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
/* justify-content: center; */
/* align-items: center; */
}

.settings-close-button {
position: absolute;
top: 10px;
right: 10px;
height: 1rem;
width: 1rem;
cursor: pointer;
}

.container {
justify-content: start;
align-items: center;
display: flex;
flex-direction: column;
gap: 1rem;
}
46 changes: 46 additions & 0 deletions src/components/GameSettings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState } from 'react';
import { Difficulty } from '../../game';
import './GameSettings.css';

export const GameSettings = ({
onClose,
onSave,
}: {
onClose: () => void;
onSave: (difficulty: Difficulty) => void;
}) => {
const [selectedDifficulty, setSelectedDifficulty] = useState<Difficulty>(Difficulty.EASY);

const handleChangeDifficulty = (event: React.ChangeEvent<HTMLSelectElement>) => {
const newValue = event.target.value;
setSelectedDifficulty(newValue as Difficulty);
};

return (
<dialog className="settings-dialog">
<span className="settings-close-button" onClick={onClose}>
</span>
<div className="container">

<label>Choose your difficulty:</label>

<select name="difficulty" id="difficulty" value={selectedDifficulty} onChange={handleChangeDifficulty}>
<option value={Difficulty.EASY}>Easy</option>
<option value={Difficulty.MEDIUM}>Medium</option>
<option value={Difficulty.HARD}>Hard</option>
</select>

<button
className="button"
onClick={() => {
onSave(selectedDifficulty);
onClose();
}}
>
Let's Play!
</button>
</div>
</dialog>
);
};
52 changes: 44 additions & 8 deletions src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Game {
handleClick(card: Card): Game;
resetUnmatchedCards(): Game;
reset(): Game;
resetWithDifficulty(difficulty: Difficulty): Game;
getDuration(): string;
getScore(): number;
getAttempts(): number;
Expand All @@ -13,23 +14,49 @@ export interface Game {
hasMatchAllCards(): boolean;
}

interface DifficultyLevel {
numCards: number,
matchLength: number,
}

export enum Difficulty {
EASY = 'easy',
MEDIUM = 'medium',
HARD = 'hard'
}

function getDifficultySpec(difficulty: Difficulty): DifficultyLevel {
switch (difficulty) {
case Difficulty.EASY:
return { numCards: 2, matchLength: 2 };
case Difficulty.MEDIUM:
return { numCards: 4, matchLength: 2 };
case Difficulty.HARD:
return { numCards: 8, matchLength: 2 };
}
}

interface GameData {
start: Date | null;
end: Date | null;
score: number;
attempts: number;
cards: Card[];
difficulty: Difficulty;
}

const DefaultGameData = {
start: null,
end: null,
score: 0,
attempts: 0,
cards: getInitialCards()
};
cards: getInitialCards(getDifficultySpec(Difficulty.HARD).numCards),
difficulty: Difficulty.HARD
}


export function NewGame(game: GameData = DefaultGameData): Game {

function getFaceUpCards(cards = game.cards): Card[] {
return cards.filter((c) => c.isFaceUp && !c.isMatched);
}
Expand All @@ -54,8 +81,19 @@ export function NewGame(game: GameData = DefaultGameData): Game {
end: null,
score: 0,
attempts: 0,
cards: getInitialCards()
});
cards: getInitialCards(getDifficultySpec(game.difficulty).numCards),
difficulty: game.difficulty
})
},
resetWithDifficulty(difficulty: Difficulty): Game {
return NewGame({
start: null,
end: null,
score: 0,
attempts: 0,
cards: getInitialCards(getDifficultySpec(difficulty).numCards),
difficulty: difficulty
})
},
handleClick(card: Card): Game {
if (card.isFaceUp || card.isMatched || hasFlippedTwoCards()) {
Expand Down Expand Up @@ -92,14 +130,12 @@ export function NewGame(game: GameData = DefaultGameData): Game {
resetUnmatchedCards(): Game {
return NewGame({
...game,
cards: game.cards.map((c) =>
c.isMatched ? c : { ...c, isFaceUp: false }
)
cards: game.cards.map((c) => c.isMatched ? c : ({ ...c, isFaceUp: false })),
});
},
getDuration(): string {
if (game.start === null) {
return '0m 0s';
return "0m 0s";
}

let end = new Date();
Expand Down

0 comments on commit b29e710

Please sign in to comment.