Skip to content
This repository was archived by the owner on Mar 21, 2026. It is now read-only.

Commit 334d090

Browse files
Typeword project (#55)
2 parents 1f6ee77 + d685ad3 commit 334d090

7 files changed

Lines changed: 268 additions & 1 deletion

File tree

changelog.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
added new project: crasher, update styles for tik tak toe
1+
New mayor project: TypeWord. Check it out in the projects tab

src/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { rooms } from "./routes/ttt.js";
1111
import { readFileSync, writeFileSync } from "fs";
1212
import { addClick, getClicks, getMessage, setMessage } from "./functions.js";
1313
import * as discojs from "@thenamelessdev/discojs";
14+
import { typeWordRooms } from "./routes/typeword.js";
1415

1516
//vars and conf
1617
dotenv.config();
@@ -136,6 +137,27 @@ io.on("connection", async (socket) => {
136137
io.emit("delete", ({ room: room }));
137138
delete rooms[room];
138139
});
140+
141+
socket.on("typewordstart", async (data) => {
142+
try{
143+
const response = await fetch("https://random-word-api.herokuapp.com/word");
144+
const json = await response.json();
145+
if(response.ok){
146+
io.emit("typewordstart", {"room": data.room, "word": json[0]});
147+
}
148+
else{
149+
io.emit("typewordstart", {"room": data.room, "word": "error"});
150+
}
151+
}
152+
catch{
153+
io.emit("typewordstart", {"room": data.room, "word": "error"});
154+
}
155+
});
156+
157+
socket.on("typewordfinish", (data) => {
158+
typeWordRooms[data.room].winner = data.player;
159+
io.emit("typewordwinner", {"room": data.room, "player": data.player});
160+
});
139161
});
140162

141163
server.listen({port, host: "0.0.0.0"});

src/routes/projects.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ const router = express.Router();
44
import tttRouter from "./ttt.js";
55
router.use("/ttt", tttRouter);
66

7+
import typeWordRouter from "./typeword.js";
8+
router.use("/typeword", typeWordRouter);
9+
710
router.get("/", (req: Request, res: Response) => {
811
res.render("projects");
912
});

src/routes/typeword.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import express, { Request, Response } from "express";
2+
import { verify } from "../functions.js";
3+
import { io } from "../index.js";
4+
const router = express.Router();
5+
6+
interface typeWordRoom {
7+
name: string,
8+
host: string,
9+
player?: string,
10+
winner?: string,
11+
}
12+
13+
export let typeWordRooms: Record<string, typeWordRoom> = {};
14+
15+
router.get("/", (req: Request, res: Response) => {
16+
res.render("projects/typeword/index")
17+
});
18+
19+
router.post("/create", async (req: Request, res: Response) => {
20+
const { username, room } = req.body;
21+
22+
if(username && room){
23+
if(await verify(req)){
24+
if(typeWordRooms[room]){
25+
res.render("error", { error: "Room name already exists" });
26+
}
27+
else{
28+
typeWordRooms[room] = {
29+
name: room,
30+
host: username
31+
}
32+
res.render("projects/typeword/game", { room: room, user: username, host: username, player: "none" });
33+
}
34+
}
35+
else{
36+
res.render("error", { error: "Cloudflare turnstile failed" });
37+
}
38+
}
39+
else{
40+
res.status(400).render("error", { error: "missing username or room name" });
41+
}
42+
});
43+
44+
router.post("/join", async (req: Request, res: Response) => {
45+
const { username, room } = req.body;
46+
47+
if(username && room){
48+
if(await verify(req)){
49+
if(typeWordRooms[room]){
50+
typeWordRooms[room].player = username;
51+
io.emit("typewordjoin", {"room": room, "username": username});
52+
res.render("projects/typeword/game", { room: room, user: username, host: typeWordRooms[room].host, player: username });
53+
}
54+
else{
55+
res.status(404).render("error", { error: "Room doesn't exists" })
56+
}
57+
}
58+
else{
59+
res.render("error", { error: "Cloudflare turnstile failed" });
60+
}
61+
}
62+
else{
63+
res.status(400).render("error", { error: "missing username or room name" });
64+
}
65+
});
66+
67+
export default router;

views/projects.ejs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
<br>
4141
<a href="/projects/crasher">Crasher</a>
4242
<br>
43+
<a href="/projects/typeword">TypeWord</a>
44+
<br>
4345
<br>
4446
<p>&copy; 2025 thenamelessdev. Licensed under the <a href="https://opensource.org/license/mit">MIT license.</a></p>
4547
<p>By using the website you aggree to our <a href="/privacy">privacy policy</a></p>

views/projects/typeword/game.ejs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>TypeWord - <%= room %></title>
7+
<link rel="shortcut icon" href="/logo" type="image/x-icon">
8+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
9+
crossorigin="anonymous">
10+
</head>
11+
<body class="text-center">
12+
<h1><%= room %></h1>
13+
<p>Host: <%= host %></p>
14+
<p>You: <%= user %></p>
15+
<p id="playerText">Player: <%= player %></p>
16+
<hr>
17+
18+
<p><b>Word:</b></p>
19+
<p id="wordText"></p>
20+
<div class="d-flex justify-content-center mt-4">
21+
<input type="text" class="form-control w-25" id="wordInp">
22+
</div>
23+
<br>
24+
<p id="timeText">Time: 0s</p>
25+
26+
27+
<br>
28+
29+
<div>
30+
<button id="startGameBtn" class="btn btn-success" disabled>Start</button>
31+
</div>
32+
<script src="/socket.io/socket.io.js"></script>
33+
<script>
34+
const you = "<%= user %>";
35+
const room = "<%= room %>";
36+
const host = "<%= host %>";
37+
const socket = io();
38+
const playerText = document.getElementById("playerText");
39+
const wordText = document.getElementById("wordText");
40+
const startGameBtn = document.getElementById("startGameBtn");
41+
const timeText = document.getElementById("timeText");
42+
const wordInp = document.getElementById("wordInp");
43+
let word;
44+
let time = 0;
45+
46+
if(you == host){
47+
startGameBtn.disabled = false;
48+
}
49+
50+
function startGame(){
51+
socket.emit("typewordstart", {"room": room});
52+
}
53+
54+
function checkWord(){
55+
if(wordInp.value == word){
56+
const finishTime = time;
57+
time = undefined;
58+
socket.emit("typewordfinish", {"room": room, "time": finishTime, "player": you});
59+
}
60+
}
61+
62+
setInterval(() => {
63+
if(word){
64+
time = time + 0.01;
65+
timeText.textContent = `Time: ${time.toFixed(2)}`;
66+
checkWord();
67+
}
68+
}, 10);
69+
70+
71+
socket.on("typewordjoin", (data) => {
72+
if(data.room == room){
73+
playerText.textContent = `Player: ${data.username}`;
74+
}
75+
});
76+
77+
socket.on("typewordwinner", (data) => {
78+
if(data.room = room){
79+
word = undefined;
80+
time = 0;
81+
timeText.textContent = `Winner: ${data.player}`;
82+
}
83+
});
84+
85+
socket.on("typewordstart", (data) => {
86+
if(data.room == room){
87+
wordText.textContent = "3";
88+
setTimeout(() => {
89+
wordText.textContent = "2";
90+
setTimeout(() => {
91+
wordText.textContent = "1";
92+
setTimeout(() => {
93+
word = data.word;
94+
wordText.textContent = word;
95+
}, 1000);
96+
}, 1000);
97+
}, 1000);
98+
}
99+
});
100+
101+
startGameBtn.addEventListener("click", () => startGame());
102+
</script>
103+
</body>
104+
</html>

views/projects/typeword/index.ejs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>TypeWord</title>
7+
<link rel="shortcut icon" href="/logo" type="image/x-icon">
8+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
9+
crossorigin="anonymous">
10+
<script
11+
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
12+
async
13+
defer
14+
></script>
15+
</head>
16+
<body class="text-center">
17+
<h1>TypeWord</h1>
18+
<p><b>A fun game to play with friends</b></p>
19+
<p>How to play: after both players join the room the game will give you a world. You have to type the word as fast as you can and whoewer types it faster wins.</p>
20+
<hr>
21+
<div>
22+
<h5>Create Room</h5>
23+
<form action="/projects/typeword/create" method="post">
24+
<div class="d-flex justify-content-center mt-4">
25+
<div class="form-floating w-25">
26+
<input type="text" name="room" id="room" class="form-control" placeholder="Room name" required>
27+
<label for="room">Room Name</label>
28+
</div>
29+
</div>
30+
31+
<div class="d-flex justify-content-center mt-4">
32+
<div class="form-floating w-25">
33+
<input type="text" name="username" id="username" class="form-control" placeholder="Username" required>
34+
<label for="room">Username</label>
35+
</div>
36+
</div>
37+
<br>
38+
<div class="cf-turnstile" data-sitekey="0x4AAAAAACAX5hJ5rxxG4bFv"></div>
39+
<br>
40+
<input type="submit" value="Create Room" class="btn btn-success">
41+
</form>
42+
</div>
43+
44+
<br>
45+
46+
<div>
47+
<h5>Join Room</h5>
48+
<form action="/projects/typeword/join" method="post">
49+
<div class="d-flex justify-content-center mt-4">
50+
<div class="form-floating w-25">
51+
<input type="text" name="room" id="room" class="form-control" placeholder="Room name" required>
52+
<label for="room">Room Name</label>
53+
</div>
54+
</div>
55+
56+
<div class="d-flex justify-content-center mt-4">
57+
<div class="form-floating w-25">
58+
<input type="text" name="username" id="username" class="form-control" placeholder="Username" required>
59+
<label for="room">Username</label>
60+
</div>
61+
</div>
62+
<br>
63+
<div class="cf-turnstile" data-sitekey="0x4AAAAAACAX5hJ5rxxG4bFv"></div>
64+
<br>
65+
<input type="submit" value="Join Room" class="btn btn-success">
66+
</form>
67+
</div>
68+
</body>
69+
</html>

0 commit comments

Comments
 (0)