puzzle visualized, added puzzle validator on server, and uploading before storm wrecks my shit
This commit is contained in:
parent
d213fced52
commit
01ca3f4d93
26
README.md
26
README.md
@ -1,3 +1,27 @@
|
|||||||
# WheelOfFortune
|
# WheelOfFortune
|
||||||
|
|
||||||
a webgame to play wheel of fortune with friends
|
## a webgame to play wheel of fortune with friends
|
||||||
|
|
||||||
|
when creating puzzles make sure there are no words greater than 12 characters.
|
||||||
|
make sure you specify 4 slots on the answer property in array format like so:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"given": ["a", "b", "r"],
|
||||||
|
"answer": ["", "Andy and", "the Backs", ""],
|
||||||
|
"category": "alternative names"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
not doing this will give you an error.
|
||||||
|
|
||||||
|
you can see the format in puzzles.json
|
||||||
|
|
||||||
|
if you have a one line puzzle you can shorten it:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"given": ["a", "b", "r"],
|
||||||
|
"answer": "Amerimutt",
|
||||||
|
"category": "alternative names"
|
||||||
|
}
|
||||||
|
```
|
129
index.html
129
index.html
@ -12,7 +12,50 @@
|
|||||||
.div-shadow {
|
.div-shadow {
|
||||||
box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
|
box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
|
||||||
}
|
}
|
||||||
|
.puzzleboard {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12, 1fr); /* 12 columns, equal width */
|
||||||
|
grid-template-rows: repeat(4, auto); /* 4 rows, height adjusts to content */
|
||||||
|
gap: 1px; /* Optional: spacing between grid items */
|
||||||
|
height: 205px;
|
||||||
|
width: 500px;
|
||||||
|
max-width: 100%; /* Optional: ensures grid fits within the container */
|
||||||
|
max-height: 100%; /* Optional: ensures the grid respects height limits */
|
||||||
|
}
|
||||||
|
.grid-item {
|
||||||
|
background-image: radial-gradient(circle,#21D375 ,green); /* For visualization */
|
||||||
|
border: 1px solid black; /* Optional: visual boundary for items */
|
||||||
|
text-align: center;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.grid-item-2 {
|
||||||
|
background-color: white; /* For visualization */
|
||||||
|
border: 1px solid black; /* Optional: visual boundary for items */
|
||||||
|
text-align: center;
|
||||||
|
padding: 1px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size:1.2rem;
|
||||||
|
font-family:'sans';
|
||||||
|
font-weight:bold;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.category {
|
||||||
|
background-image: radial-gradient(circle,#1f57dd ,#0000ae);
|
||||||
|
color: white;
|
||||||
|
max-width: 500px;
|
||||||
|
font-size:1.2rem;
|
||||||
|
font-family:'sans';
|
||||||
|
font-weight:bold;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
/* Dialog styling */
|
/* Dialog styling */
|
||||||
dialog {
|
dialog {
|
||||||
border: none;
|
border: none;
|
||||||
@ -54,7 +97,7 @@
|
|||||||
<button id="create-room">Create Room</button>
|
<button id="create-room">Create Room</button>
|
||||||
<input id="join-code" placeholder="Enter room code">
|
<input id="join-code" placeholder="Enter room code">
|
||||||
<button id="join-room">Join Room</button>
|
<button id="join-room">Join Room</button>
|
||||||
|
<div class="room-code"></div>
|
||||||
<div class="flx(wrap) mb-2 players"></div>
|
<div class="flx(wrap) mb-2 players"></div>
|
||||||
|
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
@ -62,6 +105,11 @@
|
|||||||
<button id="spin-wheel" style="display: none;">Spin Wheel</button>
|
<button id="spin-wheel" style="display: none;">Spin Wheel</button>
|
||||||
<input id="guess-letter" placeholder="Guess a letter" style="display: none;">
|
<input id="guess-letter" placeholder="Guess a letter" style="display: none;">
|
||||||
<button id="guess-button" style="display: none;">Guess</button>
|
<button id="guess-button" style="display: none;">Guess</button>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="puzzleboard mb-2"></div>
|
||||||
|
<div class="category mb-5"></div>
|
||||||
|
|
||||||
<div style="display:relative;">
|
<div style="display:relative;">
|
||||||
<span style="position: absolute; top: 0; left:50%">
|
<span style="position: absolute; top: 0; left:50%">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>arrow-down-bold</title><path d="M9,4H15V12H19.84L12,19.84L4.16,12H9V4Z" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>arrow-down-bold</title><path d="M9,4H15V12H19.84L12,19.84L4.16,12H9V4Z" /></svg>
|
||||||
@ -288,6 +336,66 @@
|
|||||||
players.innerHTML = playersHtml;
|
players.innerHTML = playersHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawPuzzle(message, gridRows = 4, gridCols = 12) {
|
||||||
|
//legend null = space, "" = white space to guess, "a" is a letter already solved for you.
|
||||||
|
console.log(message);
|
||||||
|
//wheel of fortune puzzles must fit within a 12x4 grid of letter spaces
|
||||||
|
//first we draw the spaces
|
||||||
|
let puzzle = message.puzzle.puzzle;
|
||||||
|
let puzzleBoard = document.querySelector('.puzzleboard');
|
||||||
|
let resultingPuzzleBoard = [];
|
||||||
|
//if the first entry of the puzzle is an array and not a string display the puzzle differently
|
||||||
|
if (Array.isArray(message.puzzle.puzzle[0])) {
|
||||||
|
console.log('first entry is an array in this puzzle...');
|
||||||
|
}
|
||||||
|
else if (typeof message.puzzle.puzzle[0] == 'string') {
|
||||||
|
console.log('first entry is a string in this puzzle...');
|
||||||
|
//if we have a single string we need to return it on the second line from the bottom
|
||||||
|
|
||||||
|
resultingPuzzleBoard.push(Array(12).fill(null));
|
||||||
|
resultingPuzzleBoard.push(Array(12).fill(null));
|
||||||
|
|
||||||
|
const nextLine = Array(12).fill(null);
|
||||||
|
const startIndex = Math.floor((12 - message.puzzle.puzzle.length) / 2);
|
||||||
|
console.log(startIndex);
|
||||||
|
for (let i = 0; i < message.puzzle.puzzle.length; i++) {
|
||||||
|
nextLine[startIndex + i] = message.puzzle.puzzle[i];
|
||||||
|
}
|
||||||
|
console.log(nextLine);
|
||||||
|
resultingPuzzleBoard.push(nextLine);
|
||||||
|
resultingPuzzleBoard.push(Array(12).fill(null));
|
||||||
|
console.log(resultingPuzzleBoard);
|
||||||
|
//after we've drawn our data for our board,now its time to visualize it to the user
|
||||||
|
for(let i of resultingPuzzleBoard) {
|
||||||
|
for (let j in i) {
|
||||||
|
const letter = i[j];
|
||||||
|
if (letter == null || letter == ' ') {
|
||||||
|
let el = document.createElement('div');
|
||||||
|
el.classList.add('grid-item');
|
||||||
|
el.innerHTML = ' ';
|
||||||
|
// el.style.height = '50px';
|
||||||
|
// el.style.width = '50px';
|
||||||
|
// el.style.border = 'solid'
|
||||||
|
puzzleBoard.appendChild(el);
|
||||||
|
}
|
||||||
|
else if (letter != null && letter != ' ') {
|
||||||
|
let el = document.createElement('div');
|
||||||
|
el.classList.add('grid-item-2');
|
||||||
|
el.innerText = letter;
|
||||||
|
puzzleBoard.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('error: ','message does not have correct datatype of string or array');
|
||||||
|
}
|
||||||
|
//finally draw the category!
|
||||||
|
document.querySelector('.category').classList.add('pt-2','pb-2');
|
||||||
|
document.querySelector('.category').innerHTML = `${message.puzzle.category}`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function changeName() {
|
function changeName() {
|
||||||
console.log('change name hit.');
|
console.log('change name hit.');
|
||||||
const nameDialog = document.querySelector('.nameDialog');
|
const nameDialog = document.querySelector('.nameDialog');
|
||||||
@ -306,13 +414,19 @@
|
|||||||
}
|
}
|
||||||
const socket = new WebSocket("ws://localhost:8080");
|
const socket = new WebSocket("ws://localhost:8080");
|
||||||
|
|
||||||
document.getElementById("create-room").onclick = () => {
|
document.getElementById("create-room").onclick = function () {
|
||||||
socket.send(JSON.stringify({ type: "create_room" }));
|
socket.send(JSON.stringify({ type: "create_room" }));
|
||||||
|
this.style.display = 'none';
|
||||||
|
document.getElementById("join-room").style.display = 'none';
|
||||||
|
document.getElementById('join-code').style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("join-room").onclick = () => {
|
document.getElementById("join-room").onclick = () => {
|
||||||
const roomCode = document.getElementById("join-code").value;
|
const roomCode = document.getElementById("join-code").value;
|
||||||
socket.send(JSON.stringify({ type: "join_room", roomCode }));
|
socket.send(JSON.stringify({ type: "join_room", roomCode }));
|
||||||
|
this.style.display = 'none';
|
||||||
|
document.getElementById("create-room").style.display = 'none';
|
||||||
|
document.getElementById('join-code').style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("start-game").onclick = () => {
|
document.getElementById("start-game").onclick = () => {
|
||||||
@ -336,7 +450,8 @@
|
|||||||
|
|
||||||
if (message.type === "room_created") {
|
if (message.type === "room_created") {
|
||||||
window.you = message.you;
|
window.you = message.you;
|
||||||
document.getElementById("status").innerText = `Room created! Code: ${message.roomCode}`;
|
document.querySelector(".room-code").innerText =`Room Code: ${message.roomCode}`;
|
||||||
|
document.getElementById("status").innerText = `Room created!`;
|
||||||
drawPlayers(message);
|
drawPlayers(message);
|
||||||
if (message.isLeader) {
|
if (message.isLeader) {
|
||||||
document.getElementById("start-game").style.display = "inline";
|
document.getElementById("start-game").style.display = "inline";
|
||||||
@ -359,7 +474,8 @@
|
|||||||
const status = message.isLeader ? "You are the party leader!" : "You joined the room!";
|
const status = message.isLeader ? "You are the party leader!" : "You joined the room!";
|
||||||
// document.querySelector('.players').innerHtml += `<span>${}<span>`
|
// document.querySelector('.players').innerHtml += `<span>${}<span>`
|
||||||
|
|
||||||
document.getElementById("status").innerText = `Room Code: ${message.roomCode} - ${status}`;
|
document.getElementById("status").innerText = `${status}`;
|
||||||
|
document.querySelector(".room-code").innerText =`Room Code: ${message.roomCode}`;
|
||||||
if (message.isLeader) {
|
if (message.isLeader) {
|
||||||
document.getElementById("start-game").style.display = "inline";
|
document.getElementById("start-game").style.display = "inline";
|
||||||
document.getElementById("spin-wheel").style.display = "inline";
|
document.getElementById("spin-wheel").style.display = "inline";
|
||||||
@ -369,7 +485,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === "game_started") {
|
if (message.type === "game_started") {
|
||||||
console.log(message);
|
let result = drawPuzzle(message);
|
||||||
|
console.log(result);
|
||||||
document.getElementById("status").innerText = "The game has started!";
|
document.getElementById("status").innerText = "The game has started!";
|
||||||
document.getElementById("start-game").style.display = "none";
|
document.getElementById("start-game").style.display = "none";
|
||||||
document.getElementById("start-game").style.display = "inline";
|
document.getElementById("start-game").style.display = "inline";
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
"given": ["a","b","r"],
|
"given": ["a","b","r"],
|
||||||
"answer": "Wet Back",
|
"answer": "Wet Back",
|
||||||
"category":"alternative names"
|
"category":"alternative names"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": ["a","b","r"],
|
||||||
|
"answer": ["","Wetting", "the Backs","Wet Backs"],
|
||||||
|
"category":"alternative names"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
9
puzzles2.json
Normal file
9
puzzles2.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"given": ["a","b","r"],
|
||||||
|
"answer": ["","Wetting", "the Backs","Wet Backs"],
|
||||||
|
"category":"alternative names"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
98
server.js
98
server.js
@ -11,6 +11,31 @@ const wheel = [
|
|||||||
300,600,300,500,800,550,400,300,900,500,'spin again',
|
300,600,300,500,800,550,400,300,900,500,'spin again',
|
||||||
900,'Bankrupt',600,400,300
|
900,'Bankrupt',600,400,300
|
||||||
] //represents wheel in wheel of fortune game.
|
] //represents wheel in wheel of fortune game.
|
||||||
|
|
||||||
|
//make sure this is run so puzzles are easier to contruct
|
||||||
|
function checkPuzzleData(puzzles) {
|
||||||
|
for(let puzzle in puzzles) {
|
||||||
|
let i = puzzle;
|
||||||
|
puzzle = puzzles[puzzle];
|
||||||
|
if (Array.isArray(puzzle.answer)) {
|
||||||
|
if (puzzle.answer.length != 4) {
|
||||||
|
throw new Error(`
|
||||||
|
puzzle at index ${i} doesn't have the correct amount of slots defined. The proper amount of slots is 4, and you have ${puzzle.answer.length}. use "" to denote an empty line.
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
for (let p of puzzle.answer){
|
||||||
|
if (p.length > 12) {
|
||||||
|
throw new Error(`${p} (${p.length} characters long) is longer than 12 characters. only twelve characters allowed per line`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof puzzle.answer == 'string') {
|
||||||
|
if (puzzle.answer.length > 12) {
|
||||||
|
throw new Error(`${puzzle.answer} (${puzzle.answer.length} characters long) is longer than 12 characters. only twelve characters allowed per line`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
function getRandomValue(array) {
|
function getRandomValue(array) {
|
||||||
const randomIndex = crypto.randomInt(0, array.length);
|
const randomIndex = crypto.randomInt(0, array.length);
|
||||||
return array[randomIndex];
|
return array[randomIndex];
|
||||||
@ -38,39 +63,66 @@ function getRandomName() {
|
|||||||
return NameIndex === 0 ? DefaultNames[NameIndex] : DefaultNames[NameIndex-1];
|
return NameIndex === 0 ? DefaultNames[NameIndex] : DefaultNames[NameIndex-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function loadCurrentPuzzle(gameStateObject) {
|
function loadCurrentPuzzle(gameStateObject) {
|
||||||
console.log(gameStateObject);
|
console.log(gameStateObject);
|
||||||
|
let letterArray;
|
||||||
|
//make sure we only show whats given in the letterArray, and empty values for everything else
|
||||||
|
function processPuzzle(letterArray,given) {
|
||||||
|
let puzzleArray = [];
|
||||||
|
for (let letter of letterArray) {
|
||||||
|
if (letter == ' ') {
|
||||||
|
puzzleArray.push(' ');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
letterFound = false;
|
||||||
|
for (let g of given ) {
|
||||||
|
if(g.toUpperCase() == letter.toUpperCase()) {
|
||||||
|
puzzleArray.push(g);
|
||||||
|
letterFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!letterFound) {puzzleArray.push('');}
|
||||||
|
}
|
||||||
|
console.log('in loadCurrentPuzzle: ',puzzleArray);
|
||||||
|
return puzzleArray;
|
||||||
|
}
|
||||||
|
|
||||||
//calculate the puzzle for now we'll just go through however many iterations there are
|
//calculate the puzzle for now we'll just go through however many iterations there are
|
||||||
//for each slot
|
//for each slot
|
||||||
let letterArray = gameStateObject.puzzles[gameStateObject.puzzleLevel].answer.split('');
|
if (Array.isArray(gameStateObject.puzzles[gameStateObject.puzzleLevel].answer)) {
|
||||||
const given =gameStateObject.puzzles[gameStateObject.puzzleLevel].given;
|
let formattedPuzzle = [];
|
||||||
console.log('letterArray',letterArray);
|
for (let i of gameStateObject.puzzles[gameStateObject.puzzleLevel].answer) {
|
||||||
let puzzleArray = [];
|
letterArray = i.split('');
|
||||||
for (let letter of letterArray) {
|
const given = gameStateObject.puzzles[gameStateObject.puzzleLevel].given;
|
||||||
if (letter == ' ') {
|
formattedPuzzle.push(processPuzzle(letterArray,given));
|
||||||
puzzleArray.push(' ');
|
|
||||||
}
|
}
|
||||||
letterFound = false;
|
return {
|
||||||
for (let g of given ) {
|
'puzzleLevel':gameStateObject.puzzleLevel,
|
||||||
if(g.toUpperCase() == letter.toUpperCase()) {
|
'given':gameStateObject.puzzles[gameStateObject.puzzleLevel].given,
|
||||||
puzzleArray.push(g);
|
'category':gameStateObject.puzzles[gameStateObject.puzzleLevel].category,
|
||||||
letterFound = true;
|
'puzzle':formattedPuzzle,
|
||||||
}
|
'levelsRemaining':(gameStateObject.puzzles.length - 1 - gameStateObject.puzzleLevel)
|
||||||
}
|
}
|
||||||
if (!letterFound) {puzzleArray.push('');}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log(puzzleArray);
|
else {
|
||||||
gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle = puzzleArray;
|
letterArray = gameStateObject.puzzles[gameStateObject.puzzleLevel].answer.split('');
|
||||||
return {
|
const given = gameStateObject.puzzles[gameStateObject.puzzleLevel].given;
|
||||||
'puzzleLevel':gameStateObject.puzzleLevel,
|
gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle = processPuzzle(letterArray, given);
|
||||||
'given':gameStateObject.puzzles[gameStateObject.puzzleLevel].given,
|
//console.log(gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle);
|
||||||
'category':gameStateObject.puzzles[gameStateObject.puzzleLevel].category,
|
|
||||||
'puzzle':puzzleArray,
|
return {
|
||||||
'levelsRemaining':(gameStateObject.puzzles.length - 1 - gameStateObject.puzzleLevel)
|
'puzzleLevel':gameStateObject.puzzleLevel,
|
||||||
|
'given':gameStateObject.puzzles[gameStateObject.puzzleLevel].given,
|
||||||
|
'category':gameStateObject.puzzles[gameStateObject.puzzleLevel].category,
|
||||||
|
'puzzle':gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle,
|
||||||
|
'levelsRemaining':(gameStateObject.puzzles.length - 1 - gameStateObject.puzzleLevel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkPuzzleData(puzzles);
|
||||||
|
|
||||||
// server.js
|
// server.js
|
||||||
wss.on("connection", (ws) => {
|
wss.on("connection", (ws) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user