Skip to content

Commit

Permalink
Start on daily palettes and symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
matthijsgroen committed Jan 8, 2025
1 parent 8aa1499 commit de4dd2b
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 11 deletions.
29 changes: 29 additions & 0 deletions src/game/themes/daily.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, it } from "vitest";

import { generateColorMap } from "./daily";

describe(generateColorMap, () => {
it("generates a color map based on a main color", () => {
const mainColor = "#000099";
const colorMap = generateColorMap(mainColor);

expect(colorMap).toEqual({

Check failure on line 10 in src/game/themes/daily.spec.ts

View workflow job for this annotation

GitHub Actions / build

src/game/themes/daily.spec.ts > generateColorMap > generates a color map based on a main color

AssertionError: expected { white: '#7878ff', …(15) } to deeply equal { white: '#000099', …(15) } - Expected + Received Object { - "aqua": "#000099", + "aqua": "#000069", "black": "#000099", - "blue": "#000099", - "brown": "#000099", - "darkblue": "#000099", - "darkgreen": "#000099", - "gray": "#000099", - "green": "#000099", - "lightyellow": "#000099", - "orange": "#000099", - "pink": "#000099", - "purple": "#000099", - "red": "#000099", - "turquoise": "#000099", - "white": "#000099", - "yellow": "#000099", + "blue": "#3030c9", + "brown": "#000039", + "darkblue": "#000009", + "darkgreen": "#000021", + "gray": "#000000", + "green": "#000081", + "lightyellow": "#000000", + "orange": "#000000", + "pink": "#000051", + "purple": "#1818b1", + "red": "#6060f9", + "turquoise": "#000000", + "white": "#7878ff", + "yellow": "#4848e1", } ❯ src/game/themes/daily.spec.ts:10:22
white: "#000099",
red: "#000099",
yellow: "#000099",
blue: "#000099",
purple: "#000099",
black: "#000099",
green: "#000099",
aqua: "#000099",
darkgreen: "#000099",
darkblue: "#000099",
brown: "#000099",
pink: "#000099",
turquoise: "#000099",
orange: "#000099",
lightyellow: "#000099",
gray: "#000099"
});
});
});
46 changes: 41 additions & 5 deletions src/game/themes/daily.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { lighten } from "@/support/colors";

import { BlockColor } from "../types";

export const shapeMap: Record<BlockColor, string> = {
Expand All @@ -12,11 +14,11 @@ export const shapeMap: Record<BlockColor, string> = {
pink: "A",
brown: "B",
darkgreen: "C",
darkblue: "",
turquoise: "🔺",
orange: "🦋",
lightyellow: "☀️",
gray: "🐟"
darkblue: "D",
turquoise: "E",
orange: "F",
lightyellow: "G",
gray: "H"
};

export const colorMap: Record<BlockColor, string> = {
Expand All @@ -37,3 +39,37 @@ export const colorMap: Record<BlockColor, string> = {
lightyellow: "#f9c74f",
gray: "#718096"
};

const orderedKeys: BlockColor[] = Object.keys(shapeMap) as BlockColor[];

/**
* Generate a color map based on a main color, which will be the middle shape, where the first colors
* are lighter and the last colors are darker.
*
* @param mainColor a hex string for a color, e.g. #000099
*/
export const generateColorMap = (
mainColor: string
): Record<BlockColor, string> => {
const colorMap = Object.fromEntries(
orderedKeys.map<[BlockColor, string]>((k) => [k, mainColor])
) as unknown as Record<BlockColor, string>;

// There are 16 colors, where the middle one is the main color
for (let i = 0; i < orderedKeys.length; i++) {
const color = orderedKeys[i];
const offset = i - 5;
colorMap[color] = lighten(mainColor, Math.round(-offset * 24));
}
return colorMap;
};

export const generateShapeMap = (
symbol: string
): Record<BlockColor, string> => {
const colorMap = Object.fromEntries(
orderedKeys.map<[BlockColor, string]>((k) => [k, symbol])
) as unknown as Record<BlockColor, string>;

return colorMap;
};
54 changes: 51 additions & 3 deletions src/game/themes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { filterInRange, RangedItem } from "@/support/schedule";

import { BlockColor } from "../types";

import { colorMap as colorMapDaily, shapeMap as shapeMapDaily } from "./daily";
import {
colorMap as colorMapDaily,
generateColorMap,
generateShapeMap,
shapeMap as shapeMapDaily
} from "./daily";
import { colorMap, shapeMap } from "./default";
import { colorMap as colorMapFall, shapeMap as shapeMapFall } from "./fall";
import {
Expand All @@ -27,8 +32,25 @@ export type BlockTheme =
| "summer"
| "daily";

type Occasion = {
date: Date;
name: string;
symbol: string;
mainColor: string;
};

const occasions: Occasion[] = [
{
name: "Valentine",
date: new Date("2022-02-14"),
symbol: "💖",
mainColor: "#A00000"
}
];

export const getShapeMapping = (
theme: BlockTheme
theme: BlockTheme,
date?: Date
): Record<BlockColor, string> => {
const mapping: Record<BlockTheme, Record<BlockColor, string>> = {
default: shapeMap,
Expand All @@ -38,11 +60,24 @@ export const getShapeMapping = (
summer: shapeMapSummer,
daily: shapeMapDaily
};
if (theme === "daily" && date) {
const occasion = occasions.find((occasion) => {
return (
occasion.date.getMonth() === date.getMonth() &&
occasion.date.getDate() === date.getDate()
);
});

if (occasion) {
return generateShapeMap(occasion.symbol);
}
}
return mapping[theme];
};

export const getColorMapping = (
theme: BlockTheme
theme: BlockTheme,
date?: Date
): Record<BlockColor, string> => {
const mapping: Record<BlockTheme, Record<BlockColor, string>> = {
default: colorMap,
Expand All @@ -52,6 +87,19 @@ export const getColorMapping = (
summer: colorMapSummer,
daily: colorMapDaily
};
if (theme === "daily" && date) {
const occasion = occasions.find((occasion) => {
return (
occasion.date.getMonth() === date.getMonth() &&
occasion.date.getDate() === date.getDate()
);
});

if (occasion) {
return generateColorMap(occasion.mainColor);
}
}

return mapping[theme];
};

Expand Down
14 changes: 14 additions & 0 deletions src/support/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const lighten = (mainColor: string, amount: number): string => {
const color = mainColor.startsWith("#") ? mainColor.slice(1) : mainColor;
const num = parseInt(color, 16);

let r = (num >> 16) + amount;
let g = ((num >> 8) & 0x00ff) + amount;
let b = (num & 0x0000ff) + amount;

r = Math.min(255, Math.max(0, r));
g = Math.min(255, Math.max(0, g));
b = Math.min(255, Math.max(0, b));

return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`;
};
8 changes: 7 additions & 1 deletion src/support/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export const filterInRange = <TItem extends RangedItem>(
return inRange(date, schedule.begin, schedule.end);
});

let currentDate = new Date();

export const fakeDate = (date: Date) => {
currentDate = date;
};

/**
* Get the current date.
*
Expand All @@ -44,4 +50,4 @@ export const filterInRange = <TItem extends RangedItem>(
* @returns the current date
*/
export const getToday = (): Date =>
process.env.NODE_ENV === "production" ? new Date() : new Date();
process.env.NODE_ENV === "production" ? new Date() : currentDate;
5 changes: 3 additions & 2 deletions src/ui/BlockColumn/BlockColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Tray } from "@/ui/Tray/Tray";
import { BlockTheme, getColorMapping, getShapeMapping } from "@/game/themes";
import { Column } from "@/game/types";
import { colPadding, rowSpans } from "@/support/grid";
import { getToday } from "@/support/schedule";
import { timesMap } from "@/support/timeMap";

import styles from "./BlockColumn.module.css";
Expand Down Expand Up @@ -118,8 +119,8 @@ export const BlockColumn: React.FC<Props> = ({
}
}, [locked]);

const activeShapeMap = getShapeMapping(theme);
const activeColorMap = getColorMapping(theme);
const activeShapeMap = getShapeMapping(theme, getToday());
const activeColorMap = getColorMapping(theme, getToday());
const handlePickup = useCallback(
(rect: DOMRect) => {
onPickUp?.({
Expand Down
75 changes: 75 additions & 0 deletions src/ui/Theme/DailyTheme.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { Meta, StoryObj } from "@storybook/react";

import { generateRandomLevel } from "@/game/level-creation/generateRandomLevel";
import { getDailySettings } from "@/game/level-types/daily";
import { mulberry32 } from "@/support/random";
import { fakeDate } from "@/support/schedule";

import { LevelLayout as LevelLayoutComponent } from "../LevelLayout/LevelLayout";

type CustomArgs = {
theme: string;
};

const occasionDates: [Date, name: string][] = [
[new Date("2022-02-14"), "valentine"],
[new Date("2022-12-25"), "christmas"],
[new Date("2022-10-31"), "halloween"],
[new Date("2022-01-01"), "newYear"],
[new Date("2022-04-22"), "earthDay"],
[new Date("2022-04-01"), "aprilFools"],
[new Date("2022-03-17"), "stPatricks"]
];

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta: Meta<CustomArgs> = {
title: "BlockSort/Theme",
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: "centered"
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ["autodocs"],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
theme: {
options: ["default", ...occasionDates.map(([, name]) => name)],
control: { type: "select" }
}
},
args: {
theme: "valentine"
}
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
};

export default meta;
type Story = StoryObj<typeof meta>;

const SEED = 123456789;

const random = mulberry32(SEED);

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const DailyTheme: Story = {
args: {},
render: (args) => {
const settings = getDailySettings(11);
const level = generateRandomLevel(settings, random);
// fake the date
const date = occasionDates.find((date) => date[1] === args.theme)?.[0];
if (date) {
fakeDate(date);
}

return (
<div className="flex w-full flex-col">
<LevelLayoutComponent
started={true}
levelState={level}
theme={"daily"}
/>
</div>
);
}
};

0 comments on commit de4dd2b

Please sign in to comment.