Skip to content

Commit

Permalink
feat: game and jyanshi information
Browse files Browse the repository at this point in the history
  • Loading branch information
shiftpsh committed Jul 16, 2024
1 parent 2fa76ec commit 0036aa9
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
11 changes: 11 additions & 0 deletions prisma/migrations/20240716180540_added_game_records/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Warnings:
- Added the required column `addedByUserId` to the `GameRecord` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE `GameRecord` ADD COLUMN `addedByUserId` INTEGER NOT NULL;

-- AddForeignKey
ALTER TABLE `GameRecord` ADD CONSTRAINT `GameRecord_addedByUserId_fkey` FOREIGN KEY (`addedByUserId`) REFERENCES `User`(`userId`) ON DELETE RESTRICT ON UPDATE CASCADE;
2 changes: 2 additions & 0 deletions prisma/migrations/20240716183224_p3_taboo/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `GameRecord` MODIFY `gameType` ENUM('P4_TOUPUUSEN', 'P3_TOUPUUSEN', 'P4_HANCHAN', 'P3_HANCHAN', 'P3_TABOO') NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `User` ADD COLUMN `lastGameAt` DATETIME(3) NULL;
19 changes: 12 additions & 7 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum GameType {
P3_TOUPUUSEN
P4_HANCHAN
P3_HANCHAN
P3_TABOO
}

enum Wind {
Expand Down Expand Up @@ -46,19 +47,23 @@ model User {
hashedPassword String?
isHost Boolean @default(false)
firstGameAt DateTime?
lastGameAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userScores UserScore[]
userScores UserScore[]
addedGameRecords GameRecord[]
}

model GameRecord {
gameId Int @id @default(autoincrement())
gameType GameType
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userScores UserScore[]
gameId Int @id @default(autoincrement())
gameType GameType
addedByUserId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
addedByUser User @relation(fields: [addedByUserId], references: [userId])
userScores UserScore[]
@@index([gameType])
@@index([createdAt])
Expand Down
2 changes: 2 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Router } from "express";
import authMiddleware from "src/middlewares/auth";
import auth from "./auth";
import record from "./record";
import site from "./site";

const router = Router();
router.use(authMiddleware);
Expand All @@ -12,5 +13,6 @@ router.get("/hello", (req, res) =>

router.use("/auth", auth);
router.use("/record", record);
router.use("/site", site);

export default router;
16 changes: 15 additions & 1 deletion src/api/record/$put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,21 @@ const handler: RequestHandler = async (req, res) => {
}

await db.gameRecord.create({
data: gameInputToGameDataCreateInput(body),
data: {
...gameInputToGameDataCreateInput(body),
addedByUserId: req.user.userId,
},
});

await db.user.updateMany({
where: {
userId: {
in: body.userScores.map((x) => x.userId),
},
},
data: {
lastGameAt: new Date(),
},
});

res.sendStatus(200);
Expand Down
15 changes: 15 additions & 0 deletions src/api/site/game_types/$get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { RequestHandler } from "express";
import { gameTypes } from "src/model/gameType/general";
import { toGameTypeDetailsResponse } from "src/model/gameType/types";

const handler: RequestHandler = (req, res) => {
res.send(
Object.entries(gameTypes)
.sort((a, b) => {
return a[1].index - b[1].index;
})
.map(([k, v]) => toGameTypeDetailsResponse(k, v))
);
};

export default handler;
8 changes: 8 additions & 0 deletions src/api/site/game_types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Router } from "express";
import $get from "./$get";

const router = Router();

router.get("/", $get);

export default router;
10 changes: 10 additions & 0 deletions src/api/site/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Router } from "express";
import game_types from "./game_types";
import jyanshis from "./jyanshis";

const router = Router();

router.use("/game_types", game_types);
router.use("/jyanshis", jyanshis);

export default router;
18 changes: 18 additions & 0 deletions src/api/site/jyanshis/$get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RequestHandler } from "express";
import db from "src/database/db";
import { toUserResponse } from "src/model/user/types";

const handler: RequestHandler = async (req, res) => {
const users = await db.user.findMany({
orderBy: {
lastGameAt: {
sort: "desc",
nulls: "last",
},
},
});

res.send(users.map((u) => toUserResponse(u)));
};

export default handler;
9 changes: 9 additions & 0 deletions src/api/site/jyanshis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Router } from "express";
import { wrap } from "src/utils/asyncWrapper";
import $get from "./$get";

const router = Router();

router.get("/", wrap($get));

export default router;
21 changes: 18 additions & 3 deletions src/model/game/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ const windRank: Record<Wind, number> = {

export const gameInputToGameDataCreateInput = (
gameInput: Static<typeof GameInputGuard>
): Prisma.GameRecordCreateInput => {
const { gameType, userScores } = gameInput;
): Omit<Prisma.GameRecordCreateInput, "addedByUser"> => {
const {
gameType,
userScores,
createdAt: createdAtInput = new Date(),
} = gameInput;
const createdAt = new Date(createdAtInput);
userScores.sort((a, b) => {
if (a.score !== b.score) return b.score - a.score;
return windRank[a.initialSeat] - windRank[b.initialSeat];
Expand Down Expand Up @@ -45,13 +50,23 @@ export const gameInputToGameDataCreateInput = (
0
),
userScoreYakuman: {
create: yakumans.map((yakuman) => ({ yakuman })),
create: yakumans.map((yakuman) => ({
yakuman,
createdAt,
updatedAt: createdAt,
})),
},
createdAt,
updatedAt: createdAt,
})),
},
createdAt,
updatedAt: createdAt,
};
}
),
},
createdAt,
updatedAt: createdAt,
};
};
26 changes: 26 additions & 0 deletions src/model/gameType/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ const RANK_UMA_HANCHAN_4P = [30, 10, -10, -30];
const RANK_UMA_HANCHAN_3P = [30, 0, -30];

export interface GameTypeDetails {
index: number;
players: number;
totalScore: number;
scoreType: "score" | "chips";
winds: Wind[];
starsPerYakuman: Record<Yakuman, number>;
uma: (score: number, rank: number) => number;
Expand All @@ -16,8 +18,10 @@ export interface GameTypeDetails {

export const gameTypes: Record<GameType, GameTypeDetails> = {
P4_HANCHAN: {
index: 0,
players: 4,
totalScore: 100000,
scoreType: "score",
winds: [Wind.EAST, Wind.SOUTH, Wind.WEST, Wind.NORTH],
starsPerYakuman: YAKUMAN_STARS_DEFAULT,
uma: (score, rank) => {
Expand All @@ -31,9 +35,27 @@ export const gameTypes: Record<GameType, GameTypeDetails> = {
ja: "四人半荘戦",
},
},
P3_TABOO: {
index: 1,
players: 3,
totalScore: 0,
scoreType: "chips",
winds: [Wind.EAST, Wind.SOUTH, Wind.WEST],
starsPerYakuman: YAKUMAN_STARS_DEFAULT,
uma: (score) => {
return score;
},
displayName: {
ko: "3인 금기전",
en: "3-player Taboo",
ja: "三人禁忌戦",
},
},
P3_HANCHAN: {
index: 100,
players: 3,
totalScore: 105000,
scoreType: "score",
winds: [Wind.EAST, Wind.SOUTH, Wind.WEST],
starsPerYakuman: YAKUMAN_STARS_DEFAULT,
uma: (score, rank) => {
Expand All @@ -48,8 +70,10 @@ export const gameTypes: Record<GameType, GameTypeDetails> = {
},
},
P4_TOUPUUSEN: {
index: 10000,
players: 4,
totalScore: 100000,
scoreType: "score",
winds: [Wind.EAST, Wind.SOUTH, Wind.WEST, Wind.NORTH],
starsPerYakuman: YAKUMAN_STARS_DEFAULT,
uma: (score, rank) => {
Expand All @@ -64,8 +88,10 @@ export const gameTypes: Record<GameType, GameTypeDetails> = {
},
},
P3_TOUPUUSEN: {
index: 10100,
players: 3,
totalScore: 105000,
scoreType: "score",
winds: [Wind.EAST, Wind.SOUTH, Wind.WEST],
starsPerYakuman: YAKUMAN_STARS_DEFAULT,
uma: (score, rank) => {
Expand Down
15 changes: 15 additions & 0 deletions src/model/gameType/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { GameTypeDetails } from "./general";

export const toGameTypeDetailsResponse = (
type: string,
{ players, totalScore, scoreType, winds, displayName }: GameTypeDetails
) => {
return {
type,
players,
totalScore,
scoreType,
winds,
displayName,
};
};
2 changes: 2 additions & 0 deletions src/model/user/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ export const toUserResponse = ({
loginId,
displayName,
isHost,
lastGameAt,
}: User) => {
return {
userId,
loginId,
displayName,
isHost,
lastGameAt: lastGameAt?.toISOString() || null,
};
};
6 changes: 5 additions & 1 deletion src/utils/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export const GameInputGuard = RecordType({
yakuman: ArrayType(ArrayType(YakumanGuard)),
})
),
}).withConstraint(({ gameType, userScores }) => {
createdAt: StringType.optional(),
}).withConstraint(({ gameType, userScores, createdAt }) => {
const { players, totalScore, winds } = gameTypes[gameType];
if (userScores.length !== players) {
return false;
Expand All @@ -52,5 +53,8 @@ export const GameInputGuard = RecordType({
if (Array.from(playerWinds).some((wind) => !winds.includes(wind))) {
return false;
}
if (createdAt && Number.isNaN(new Date(createdAt).getTime())) {
return false;
}
return true;
});

0 comments on commit 0036aa9

Please sign in to comment.