Skip to content

Commit

Permalink
Setting for random colors by default (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
RheingoldRiver authored Oct 21, 2023
1 parent 3e748f5 commit b308d57
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 25 deletions.
17 changes: 16 additions & 1 deletion src/components/GameStateProvider/GameStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ interface GameState {
setShowKeyboardIndicators: Dispatch<SetStateAction<boolean>>;
showInvalidUrlError: boolean;
setShowInvalidUrlError: Dispatch<SetStateAction<boolean>>;
defaultRandomColors: boolean;
updateDefaultRandomColors: (newDefault: boolean) => void;
}

const DEFAULT_GAME_STATE: GameState = {
Expand All @@ -67,6 +69,8 @@ const DEFAULT_GAME_STATE: GameState = {
setShowKeyboardIndicators: () => {},
showInvalidUrlError: false,
setShowInvalidUrlError: () => {},
defaultRandomColors: false,
updateDefaultRandomColors: () => {},
};

export const GameStateContext = createContext(DEFAULT_GAME_STATE);
Expand All @@ -75,14 +79,18 @@ export default function GameStateProvider({ children }: { children: ReactNode })
const [pentominoColors, setPentominoColors] = useState<Colors>(DEFAULT_CONFIG.colors);
const [surface, setSurface] = useState<Surface>(DEFAULT_CONFIG.surface);

const [defaultRandomColors, setDefaultRandomColors] = useState<boolean>(() => {
return (window.localStorage.getItem("randc") || "false") === "true";
});

const [showInvalidUrlError, setShowInvalidUrlError] = useState<boolean>(false);
const params = useParams();
const { config } = params;

useEffect(() => {
if (!config) return;
try {
const parsedConfig = deserializeUrl(config);
const parsedConfig = deserializeUrl(config, defaultRandomColors);
setGrid(parsedConfig.grid);
setSurface(parsedConfig.surface);
setPentominoColors(parsedConfig.colors);
Expand Down Expand Up @@ -267,6 +275,11 @@ export default function GameStateProvider({ children }: { children: ReactNode })
}
}

const updateDefaultRandomColors = (newDefault: boolean) => {
window.localStorage.setItem("randc", newDefault.toString());
setDefaultRandomColors(newDefault);
};

useHotkey("Control", "Z", () => {
const nextActionHistory = [...actionHistory];
const lastAction = nextActionHistory.pop();
Expand Down Expand Up @@ -359,6 +372,8 @@ export default function GameStateProvider({ children }: { children: ReactNode })
setShowKeyboardIndicators,
showInvalidUrlError,
setShowInvalidUrlError,
defaultRandomColors,
updateDefaultRandomColors,
}}
>
{children}
Expand Down
8 changes: 4 additions & 4 deletions src/components/GameStateProvider/urlConfig.legacy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ test("[legacy urls] a URL is properly decoded, asymmetry", () => {
test("[legacy urls] you get the right grid, no asymmetry", () => {
const grid: PlacedPentomino[][] = EMPTY_GRID(8, 8);
grid[4][4].pentomino = PENTOMINOES["I"];
expect(deserializeUrl("8_8I04_4")).toStrictEqual({
expect(deserializeUrl("8_8I04_4", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.Rectangle,
});
expect(deserializeUrl("8_8i044")).toStrictEqual({
expect(deserializeUrl("8_8i044", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.Rectangle,
Expand All @@ -129,12 +129,12 @@ test("[legacy urls] you get the right grid, no asymmetry", () => {
test("[legacy urls] you get the right grid, asymmetry", () => {
const grid: PlacedPentomino[][] = EMPTY_GRID(10, 6);
grid[4][6].pentomino = PENTOMINOES["I"];
expect(deserializeUrl("6_10I04_6")).toStrictEqual({
expect(deserializeUrl("6_10I04_6", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.Rectangle,
});
expect(deserializeUrl("6_10i046")).toStrictEqual({
expect(deserializeUrl("6_10i046", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.Rectangle,
Expand Down
4 changes: 2 additions & 2 deletions src/components/GameStateProvider/urlConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ test("a URL is properly decoded with a 2-digit dimension", () => {
test("you get the right grid with single-digit numbers", () => {
const grid: PlacedPentomino[][] = EMPTY_GRID(8, 8);
grid[4][4].pentomino = PENTOMINOES["I"];
expect(deserializeUrl("K88I044")).toStrictEqual({
expect(deserializeUrl("K88I044", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.KleinBottle,
Expand All @@ -136,7 +136,7 @@ test("you get the right grid with single-digit numbers", () => {
test("you get the right grid with two-digit numbers", () => {
const grid: PlacedPentomino[][] = EMPTY_GRID(10, 6);
grid[4][6].pentomino = PENTOMINOES["I"];
expect(deserializeUrl("P6aI046")).toStrictEqual({
expect(deserializeUrl("P6aI046", false)).toStrictEqual({
grid: grid,
colors: DEFAULT_COLORS,
surface: SURFACES.ProjectivePlane,
Expand Down
15 changes: 14 additions & 1 deletion src/components/GameStateProvider/urlConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {
DEFAULT_COLORS,
letterToSurface,
MAX_NUM_COLORS,
PENTOMINO_NAMES,
PlacedPentomino,
randomPentominoColors,
Surface,
SURFACES,
} from "./../../constants";
Expand Down Expand Up @@ -434,7 +436,17 @@ function decodeColor(color: string | number, p: string, legacy: boolean): number
return toNumber(color) + HALF_NUM_COLORS;
}

export function deserializeUrl(s: string): UrlConfig {
function randomizeColorsIfNeeded(settings: UrlConfig, defaultRandomColors: boolean) {
if (defaultRandomColors === false) return;
let doChange: boolean = true;
Object.values(settings.colors).forEach((c) => {
if (c !== 0) doChange = false;
});
if (!doChange) return;
settings.colors = randomPentominoColors(PENTOMINO_NAMES.length);
}

export function deserializeUrl(s: string, defaultRandomColors: boolean): UrlConfig {
const legacy = !!s[0].match(/[0-9]/);
const config = legacy === true ? decodeSurfacelessUrl(s) : decodeUrl(s);
const ret = {
Expand All @@ -460,5 +472,6 @@ export function deserializeUrl(s: string): UrlConfig {
};
if (p.p.toUpperCase() !== PENTOMINOES.R.name) ret.colors[p.p.toUpperCase()] = decodeColor(r.color, p.p, legacy);
});
randomizeColorsIfNeeded(ret, defaultRandomColors);
return ret;
}
47 changes: 31 additions & 16 deletions src/components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import { ReactNode, useContext, useState } from "react";
import { AppStateContext } from "../AppStateProvider/AppStateProvider";
import {
Colors,
DEFAULT_COLORS,
DEFAULT_DISPLAY_COLORS,
EMPTY_PENTOMINO,
MAX_DIMENSION_SIZE,
MAX_NUM_COLORS,
PENTOMINO_NAMES,
shuffleArray,
randomPentominoColors,
Surface,
SURFACES,
} from "../../constants";
Expand All @@ -32,6 +30,15 @@ interface CurrentState {
showKeyboardIndicators: boolean;
copyImage: boolean;
showCdot: boolean;
defaultRandomColors: boolean;
}

function getNumVisibleColors(numVisibleColors: number, defaultRandomColors: boolean, pentominoColors: Colors): number {
if (!defaultRandomColors) return numVisibleColors;
const maxColorValue = Object.values(pentominoColors).reduce((acc, c) => {
return Math.max(acc, c);
}, 0);
return Math.max(numVisibleColors, maxColorValue + 1);
}

export const Settings = () => {
Expand All @@ -45,19 +52,22 @@ export const Settings = () => {
setSurface,
showKeyboardIndicators,
setShowKeyboardIndicators,
defaultRandomColors,
updateDefaultRandomColors,
} = useContext(GameStateContext);

const getCurrentState = () => ({
height: grid.length,
width: grid[0].length,
pentominoSize: appPreferences.pentominoSize,
numVisibleColors: appPreferences.numVisibleColors,
numVisibleColors: getNumVisibleColors(appPreferences.numVisibleColors, defaultRandomColors, pentominoColors),
displayColors: appPreferences.displayColors,
pentominoColors,
surface,
showKeyboardIndicators,
copyImage: appPreferences.copyImage,
showCdot: appPreferences.showCdot,
defaultRandomColors,
});

const [currentState, setCurrentState] = useState<CurrentState>(getCurrentState);
Expand Down Expand Up @@ -216,18 +226,7 @@ export const Settings = () => {
</Button>
<Button
onClick={() => {
const pentominoes = [...PENTOMINO_NAMES];
shuffleArray(pentominoes);
const nextColors = pentominoes.reduce(
(acc: Colors, p, i) => {
const val = i % currentState.numVisibleColors;
acc[p] = val;
return acc;
},
{ ...DEFAULT_COLORS }
);

setCurrentState({ ...currentState, pentominoColors: nextColors });
setCurrentState({ ...currentState, pentominoColors: randomPentominoColors(currentState.numVisibleColors) });
}}
>
Randomize Distribution
Expand Down Expand Up @@ -268,6 +267,20 @@ export const Settings = () => {
}}
/>
</fieldset>
<fieldset className="flex gap-4 items-center mb-4">
<label className="text-right" htmlFor="randc">
Default to random pentomino colors
</label>
<input
className="bg-white dark:bg-slate-950"
type="checkbox"
id="randc"
checked={currentState.defaultRandomColors}
onChange={(e) => {
setCurrentState({ ...currentState, defaultRandomColors: e.target.checked });
}}
/>
</fieldset>
{/* End of settings area */}
{/* Start confirmation area */}
{(currentState.width !== grid[0].length || currentState.height !== grid.length) && (
Expand Down Expand Up @@ -297,6 +310,8 @@ export const Settings = () => {
setSurface(currentState.surface);
setPentominoColors(currentState.pentominoColors);
setShowKeyboardIndicators(currentState.showKeyboardIndicators);

updateDefaultRandomColors(currentState.defaultRandomColors);
}}
>
Save changes
Expand Down
15 changes: 14 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const DEFAULT_DISPLAY_COLORS = [
"#6b21a8",
];

export const shuffleArray = <T>(array: T[]): void => {
const shuffleArray = <T>(array: T[]): void => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
Expand All @@ -192,6 +192,19 @@ export const shuffleArray = <T>(array: T[]): void => {
}
};

export const randomPentominoColors = (numVisibleColors: number): Colors => {
const pentominoes = [...PENTOMINO_NAMES];
shuffleArray(pentominoes);
return pentominoes.reduce(
(acc: Colors, p, i) => {
const val = i % numVisibleColors;
acc[p] = val;
return acc;
},
{ ...DEFAULT_COLORS }
);
};

interface ActionPentomino {
prevName: string;
prevRotation: number;
Expand Down

0 comments on commit b308d57

Please sign in to comment.