Moteur de simulation (plan 1/2) #1
No files matched your search
@@ -0,0 +1,24 @@
|
||||
[
|
||||
{ "id": "tetris", "title": "Tetris", "console": "GB", "releaseDate": "1990-09-28", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Puzzle", "basePopularity": 95 },
|
||||
{ "id": "super-mario-land", "title": "Super Mario Land", "console": "GB", "releaseDate": "1990-09-28", "launchPriceFrancs": 249, "publisher": "Nintendo", "genre": "Plateforme", "basePopularity": 95 },
|
||||
{ "id": "tennis", "title": "Tennis", "console": "GB", "releaseDate": "1990-09-28", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Sport", "basePopularity": 50, "uncertain": true },
|
||||
{ "id": "alleyway", "title": "Alleyway", "console": "GB", "releaseDate": "1990-09-28", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Casse-briques", "basePopularity": 40, "uncertain": true },
|
||||
{ "id": "baseball", "title": "Baseball", "console": "GB", "releaseDate": "1990-09-28", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Sport", "basePopularity": 30, "uncertain": true },
|
||||
{ "id": "golf", "title": "Golf", "console": "GB", "releaseDate": "1990-11-15", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Sport", "basePopularity": 45, "uncertain": true },
|
||||
{ "id": "kwirk", "title": "Kwirk", "console": "GB", "releaseDate": "1990-10-15", "launchPriceFrancs": 199, "publisher": "Acclaim", "genre": "Puzzle", "basePopularity": 30, "uncertain": true },
|
||||
{ "id": "bugs-bunny-crazy-castle", "title": "The Bugs Bunny Crazy Castle", "console": "GB", "releaseDate": "1990-10-15", "launchPriceFrancs": 249, "publisher": "Kemco", "genre": "Puzzle", "basePopularity": 45, "uncertain": true },
|
||||
{ "id": "castlevania-the-adventure", "title": "Castlevania: The Adventure", "console": "GB", "releaseDate": "1990-11-15", "launchPriceFrancs": 249, "publisher": "Konami", "genre": "Action", "basePopularity": 50, "uncertain": true },
|
||||
{ "id": "batman", "title": "Batman", "console": "GB", "releaseDate": "1990-11-15", "launchPriceFrancs": 249, "publisher": "Sunsoft", "genre": "Action", "basePopularity": 60, "uncertain": true },
|
||||
{ "id": "solar-striker", "title": "Solar Striker", "console": "GB", "releaseDate": "1990-11-15", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Shoot'em up", "basePopularity": 40, "uncertain": true },
|
||||
{ "id": "dr-mario", "title": "Dr. Mario", "console": "GB", "releaseDate": "1990-12-01", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Puzzle", "basePopularity": 60, "uncertain": true },
|
||||
{ "id": "gargoyles-quest", "title": "Gargoyle's Quest", "console": "GB", "releaseDate": "1990-12-01", "launchPriceFrancs": 249, "publisher": "Capcom", "genre": "Action", "basePopularity": 55, "uncertain": true },
|
||||
{ "id": "double-dragon", "title": "Double Dragon", "console": "GB", "releaseDate": "1990-12-01", "launchPriceFrancs": 249, "publisher": "Tradewest", "genre": "Beat'em all", "basePopularity": 55, "uncertain": true },
|
||||
{ "id": "turtles-fall-of-the-foot-clan", "title": "Teenage Mutant Hero Turtles: Fall of the Foot Clan", "console": "GB", "releaseDate": "1991-02-15", "launchPriceFrancs": 249, "publisher": "Konami", "genre": "Action", "basePopularity": 75, "uncertain": true },
|
||||
{ "id": "nemesis", "title": "Nemesis", "console": "GB", "releaseDate": "1991-03-15", "launchPriceFrancs": 249, "publisher": "Konami", "genre": "Shoot'em up", "basePopularity": 50, "uncertain": true },
|
||||
{ "id": "duck-tales", "title": "La Bande à Picsou", "console": "GB", "releaseDate": "1991-04-15", "launchPriceFrancs": 249, "publisher": "Capcom", "genre": "Plateforme", "basePopularity": 60, "uncertain": true },
|
||||
{ "id": "qix", "title": "Qix", "console": "GB", "releaseDate": "1991-05-15", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Puzzle", "basePopularity": 35, "uncertain": true },
|
||||
{ "id": "f1-race", "title": "F-1 Race", "console": "GB", "releaseDate": "1991-06-15", "launchPriceFrancs": 249, "publisher": "Nintendo", "genre": "Course", "basePopularity": 45, "uncertain": true },
|
||||
{ "id": "bubble-bobble", "title": "Bubble Bobble", "console": "GB", "releaseDate": "1991-07-15", "launchPriceFrancs": 249, "publisher": "Taito", "genre": "Plateforme", "basePopularity": 50, "uncertain": true },
|
||||
{ "id": "the-amazing-spider-man", "title": "The Amazing Spider-Man", "console": "GB", "releaseDate": "1991-09-15", "launchPriceFrancs": 249, "publisher": "LJN", "genre": "Action", "basePopularity": 50, "uncertain": true },
|
||||
{ "id": "radar-mission", "title": "Radar Mission", "console": "GB", "releaseDate": "1991-10-15", "launchPriceFrancs": 199, "publisher": "Nintendo", "genre": "Stratégie", "basePopularity": 30, "uncertain": true }
|
||||
]
|
||||
@@ -0,0 +1,57 @@
|
||||
import { compareDates, GameDate, parseISO } from './calendar';
|
||||
import { francsToCents } from './money';
|
||||
import rawGames from '../data/games.json';
|
||||
|
||||
export type ConsoleId = 'GB' | 'GBC' | 'GBA';
|
||||
|
||||
export interface Game {
|
||||
id: string;
|
||||
title: string;
|
||||
console: ConsoleId;
|
||||
releaseDate: string; // YYYY-MM-DD, sortie FR
|
||||
launchPriceCents: number;
|
||||
publisher: string;
|
||||
genre: string;
|
||||
basePopularity: number; // 0-100
|
||||
cover?: string;
|
||||
uncertain?: boolean;
|
||||
}
|
||||
|
||||
export interface RawGame extends Omit<Game, 'launchPriceCents'> {
|
||||
launchPriceFrancs: number;
|
||||
}
|
||||
|
||||
export interface Catalog {
|
||||
games: Game[];
|
||||
byId: Map<string, Game>;
|
||||
}
|
||||
|
||||
export function createCatalog(games: Game[]): Catalog {
|
||||
const byId = new Map<string, Game>();
|
||||
for (const g of games) {
|
||||
if (byId.has(g.id)) throw new Error(`Duplicate game id: ${g.id}`);
|
||||
byId.set(g.id, g);
|
||||
}
|
||||
return { games, byId };
|
||||
}
|
||||
|
||||
export function loadGames(raw: RawGame[]): Game[] {
|
||||
return raw.map(({ launchPriceFrancs, ...rest }) => ({
|
||||
...rest,
|
||||
launchPriceCents: francsToCents(launchPriceFrancs),
|
||||
}));
|
||||
}
|
||||
|
||||
export function loadDefaultCatalog(): Catalog {
|
||||
return createCatalog(loadGames(rawGames as RawGame[]));
|
||||
}
|
||||
|
||||
export function getGame(catalog: Catalog, id: string): Game {
|
||||
const game = catalog.byId.get(id);
|
||||
if (!game) throw new Error(`Unknown game: ${id}`);
|
||||
return game;
|
||||
}
|
||||
|
||||
export function availableGames(catalog: Catalog, date: GameDate): Game[] {
|
||||
return catalog.games.filter((g) => compareDates(parseISO(g.releaseDate), date) <= 0);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { availableGames, createCatalog, getGame, loadDefaultCatalog } from '../../src/sim/catalog';
|
||||
import { START_DATE, parseISO } from '../../src/sim/calendar';
|
||||
import { TEST_GAMES, testCatalog } from './helpers';
|
||||
|
||||
describe('catalog', () => {
|
||||
it('indexes games by id', () => {
|
||||
expect(getGame(testCatalog(), 'sml').title).toBe('Super Mario Land');
|
||||
});
|
||||
|
||||
it('throws on unknown id', () => {
|
||||
expect(() => getGame(testCatalog(), 'nope')).toThrow('Unknown game: nope');
|
||||
});
|
||||
|
||||
it('rejects duplicate ids', () => {
|
||||
expect(() => createCatalog([TEST_GAMES[0], TEST_GAMES[0]])).toThrow('Duplicate game id: tetris');
|
||||
});
|
||||
|
||||
it('only lists released games', () => {
|
||||
const ids = availableGames(testCatalog(), START_DATE).map((g) => g.id);
|
||||
expect(ids).toEqual(['tetris', 'sml']);
|
||||
const later = availableGames(testCatalog(), parseISO('1991-06-01')).map((g) => g.id);
|
||||
expect(later).toContain('future');
|
||||
});
|
||||
|
||||
it('loads the default GB catalog with francs converted to cents', () => {
|
||||
const catalog = loadDefaultCatalog();
|
||||
expect(catalog.games.length).toBeGreaterThanOrEqual(20);
|
||||
expect(getGame(catalog, 'super-mario-land').launchPriceCents).toBe(3796);
|
||||
for (const g of catalog.games) {
|
||||
expect(g.releaseDate).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||
expect(g.basePopularity).toBeGreaterThanOrEqual(0);
|
||||
expect(g.basePopularity).toBeLessThanOrEqual(100);
|
||||
}
|
||||
expect(availableGames(catalog, START_DATE).map((g) => g.id)).toContain('tetris');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createCatalog, Game } from '../../src/sim/catalog';
|
||||
|
||||
export const TEST_GAMES: Game[] = [
|
||||
{ id: 'tetris', title: 'Tetris', console: 'GB', releaseDate: '1990-09-28', launchPriceCents: 3034, publisher: 'Nintendo', genre: 'Puzzle', basePopularity: 90 },
|
||||
{ id: 'sml', title: 'Super Mario Land', console: 'GB', releaseDate: '1990-09-28', launchPriceCents: 3796, publisher: 'Nintendo', genre: 'Plateforme', basePopularity: 95 },
|
||||
{ id: 'future', title: 'Future Game', console: 'GB', releaseDate: '1991-06-01', launchPriceCents: 3796, publisher: 'Test', genre: 'Action', basePopularity: 50 },
|
||||
];
|
||||
|
||||
export function testCatalog() {
|
||||
return createCatalog(TEST_GAMES);
|
||||
}
|
||||
Reference in new issue
Block a user