Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ts 2023 #23

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ project/plugins/project/
*.log

# End of https://www.gitignore.io/api/sbt,scala

### TS ###
ts-rps/node_modules/*
2 changes: 2 additions & 0 deletions ts-rps/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { app } from "./routes/api";
app.listen(3000);
34 changes: 34 additions & 0 deletions ts-rps/db/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pgPromise, { ParameterizedQuery as PQ } from "pg-promise";

const cn = {
host: "localhost",
port: 5438,
database: "root",
user: "root",
password: "root",
max: 30, // use up to 30 connections
};

const pgp = pgPromise();
const db = pgp(cn);

export async function lastGame() {
return await db.oneOrNone(
"SELECT game_date, result FROM results ORDER BY game_date DESC LIMIT 1"
);
}

export async function logRes(res: string) {
const addResult = new PQ("INSERT into results(result) VALUES($1)");
return await db.none(addResult, [res]);
}

export async function allGames(): Promise<any[]> {
return await db.any(
"SELECT game_date, result FROM results ORDER BY game_date"
);
}

export function closeDB() {
db.$pool.end();
}
20 changes: 20 additions & 0 deletions ts-rps/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: "3.7"
services:
postgres:
image: postgres:12.1-alpine
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=root
- POSTGRES_DB=root
logging:
options:
max-size: 10m
max-file: "3"
ports:
- "5438:5432"
volumes:
- local_test:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
local_test:
46 changes: 46 additions & 0 deletions ts-rps/game.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { expect, test, vi } from "vitest";
import { playLogic, welcome } from "./game";
import { read } from "./model/Move";

vi.mock("./sql/db", () => {
const logRes = vi.fn();
logRes.mockResolvedValue("");
const lastGame = vi.fn();
lastGame.mockReturnValueOnce({
game_date: "2023-09-14 10:08:15.151443",
result: "blblbl",
});
lastGame.mockReturnValue({
game_date: "2023-09-14 10:08:15.151443",
result: "WIN",
});

return {
logRes,
lastGame,
};
});

test("game logic test", async () => {
expect(await playLogic("Rock", "Paper")).toContain("You lose :< ");
expect(await playLogic("Rock", "Scissors")).toContain("You Win!!!");
expect(await playLogic("Paper", "Rock")).toContain("You Win!!!");
expect(await playLogic("Paper", "Scissors")).toContain("You lose :< ");
expect(await playLogic("Scissors", "Rock")).toContain("You lose :< ");
expect(await playLogic("Scissors", "Paper")).toContain("You Win!!!");
});

test("welcome test", () => {
welcome().then((arr) => expect(arr.length).toBe(2));
welcome().then((arr) => expect(arr.length).toBe(3));
welcome().then((arr) => expect(arr[2]).toContain("WIN"));
});

test("read function test", () => {
expect(read("0")).toBe("Rock");
expect(read("1")).toBe("Paper");
expect(read("2")).toBe("Scissors");

const randomNum = String(Math.round(Math.random()) + 3);
expect(() => read(randomNum)).toThrowError();
});
54 changes: 54 additions & 0 deletions ts-rps/game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { match, P } from "ts-pattern";
import { read, Move } from "./model/Move";
import { ResultArray, resultArray, result } from "./model/Result";
import { logRes, allGames, lastGame } from "./db/db";

export function generateComputerMove(): Move {
return read(String(Math.round(Math.random() * 2)));
}
export async function playLogic(
userMove: Move,
computerMove: Move
): Promise<string> {
const [dbOutcome, messageOutcome]: string[] = match([userMove, computerMove])
.with(
["Rock", "Scissors"],
["Scissors", "Paper"],
["Paper", "Rock"],
() => ["WIN", "You Win!!!"]
)
.with(
P.when(() => userMove === computerMove),
() => ["DRAW", "It's a Draw!"]
)
.otherwise(() => ["LOSE", "You lose :< "]);
const isDbWritten = await logRes(dbOutcome);
if (isDbWritten === null) {
return messageOutcome;
} else {
return `${messageOutcome} ... but I couldn't save this result because something went wrong`;
}
}

export async function welcome(): Promise<string[]> {
const res = ["Wanna play? Your move (0: Rock, 1: Paper, 2: Scissors)"];
const lastGameParsed = result.safeParse(await lastGame());
if (lastGameParsed.success) {
res.push(
"Our last game timestamp is : " + lastGameParsed.data.game_date + "\n"
);
res.push(
"and the game finished with this result : " +
lastGameParsed.data.result +
"\n"
);
return res;
} else {
res.push("Welcome to the best RPS game ever! \n");
return res;
}
}

export async function allGamesParsed(): Promise<ResultArray> {
return resultArray.parse(await allGames());
}
5 changes: 5 additions & 0 deletions ts-rps/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
create table Results (
id SERIAL PRIMARY KEY,
result varchar NOT NULL,
game_date timestamp NOT NULL DEFAULT now()
);
21 changes: 21 additions & 0 deletions ts-rps/model/Move.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from "zod";

const moves = {
0: "Rock",
1: "Paper",
2: "Scissors",
} as const;

export const Move = z.enum(["0", "1", "2"]).transform((index) => moves[index]);
export type Move = z.infer<typeof Move>;

export function read(input: string): Move {
const res = Move.safeParse(input);
if (res.success) {
return res.data;
} else {
throw new RangeError(
"Sorry, you must enter a valid move (0, 1 or 2). Try again"
);
}
}
10 changes: 10 additions & 0 deletions ts-rps/model/Result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from "zod";

export const result = z.object({
game_date: z.coerce.date(),
result: z.enum(["WIN", "DEFEAT", "DRAW"]),
});

export type Result = z.infer<typeof result>;
export const resultArray = z.array(result);
export type ResultArray = z.infer<typeof resultArray>;
19 changes: 19 additions & 0 deletions ts-rps/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"devDependencies": {
"@types/node": "^20.6.5",
"prettier": "^3.0.3",
"typescript": "^5.2.2",
"vitest": "^0.34.5"
},
"scripts": {
"test": "vitest"
},
"dependencies": {
"@zodios/core": "^10.9.6",
"@zodios/express": "^10.6.1",
"pg-promise": "^11.5.4",
"ts-pattern": "^5.0.5",
"zod": "^3.22.2",
"zodios": "^5.1.0"
}
}
Loading