Compare commits
7 Commits
9388791cec
...
726032aeeb
Author | SHA1 | Date | |
---|---|---|---|
726032aeeb | |||
16b8315e05 | |||
737a0a95d9 | |||
7e78fe5b0d | |||
85c09d2843 | |||
5038416cd5 | |||
60ad03fbc2 |
128
README.md
128
README.md
|
@ -1,19 +1,115 @@
|
|||
<div class="centered">
|
||||
<h1>HiLo - Game Project</h1>
|
||||
<b>Name</b>: Enrique Jose Delgado Garcia <br>
|
||||
<b>ID:</b> 20220825 <br>
|
||||
<b>Email:</b> enrdel070504@kaist.ac.kr <br>
|
||||
<b>Repository link:</b> http://git.prototyping.id/20220825/HiLo_GameProject
|
||||
</div>
|
||||
# HiLo - Game Project
|
||||
**Name:** Enrique Jose Delgado Garcia
|
||||
**ID:** 20220825
|
||||
**Email:** enrdel070504@kaist.ac.kr
|
||||
**Repository link:** http://git.prototyping.id/20220825/HiLo_GameProject
|
||||
**Video demo:** https://youtu.be/LpC3dNiW6G8
|
||||
|
||||
The game works like this...
|
||||
## Game Description
|
||||
HiLo is a strategic luck-based card game, designed by Enrique (me!).
|
||||
|
||||
https://joyofcode.xyz/using-functions-from-another-svelte-component
|
||||
p5 docs
|
||||
### Premise
|
||||
The game is simple. In the game, you hold the last drawn card from a classic deck of 52 playing cards. When starting the game, you will receive a card from the deck to be your **Current Card**.
|
||||
|
||||
<style>
|
||||
.centered {
|
||||
text-align: center;
|
||||
margin-bottom: 2em
|
||||
}
|
||||
</style>
|
||||
Press "High" if you think the card on the top of the deck is higher than the **Current Card**, and press "Low" if you think the card on the top of the deck is lower than the **Current Card** (The card on the top of the deck will be referred to as the **top card** for the rest of the report).
|
||||
When either of these buttons are pressed, the card on the top of the deck is revealed. If this **top card** is higher than (or equal to) the **Current Card**, then the player will receive points based on the card's value IF they had pressed "High". If the **top card** is lower than (or equal to) the **Current Card**, the player will receive points IF they had pressed "Low". Otherwise, the player will LOSE points based on *(15 points minus the card's value)*.
|
||||
|
||||
Card values are distributed as follows:
|
||||
- Number cards
|
||||
- The value of number cards corresponds to the number displayed on the card.
|
||||
- Face cards
|
||||
- Jacks are worth 11 points, Queens are worth 12 points and Kings are worth 13 points.
|
||||
- Aces
|
||||
- Aces's value depends on if the player chose "High" or "Low" when the **top card** is an Ace. If the player chose "High", the Ace is worth 15 points. If the player chose "Low", the Ace is worth 1 point. This means, regardless of what the player chooses, the player will always win points if the **top card** is an Ace.
|
||||
- If the **Current Card** is an Ace:
|
||||
- All number cards are higher than the Ace.
|
||||
- All face cards are lower than the Ace.
|
||||
|
||||
After the score is distributed and added up, the **top card** becomes the player's **Current Card**. The game goes on until all 52 cards are exhausted from the deck.
|
||||
|
||||
Those are the basic rules of HiLo.
|
||||
|
||||
### Powers
|
||||
The game would be too simple if it was just a guessing game, so the player has a few tools at their disposal to strategize.
|
||||
- *See The Future*
|
||||
- This power (usable up to 2 times) lets the player view the top 2 cards on the deck. So, the player is able to know with 100% assurance the next two **top cards**.
|
||||
- *Reveal Suit*
|
||||
- This power (usable up to 12 times) reveals the suit of the **top card** (Hearts, Diamonds, Spades, Clubs). This can give the player some more information on the top card, and helps if they have good memory on which numbers have passed from which suits.
|
||||
- *See Remaining*
|
||||
- This power (usable up to 2 times) lets the player view the three cards that follow the top card of the deck. This gives the player information about future cards while still having them rely on their memory.
|
||||
- *Double Down*
|
||||
- This power (usable up to 7 times) doubles the points received from the next "High"/"Low" guess. If the player guesses correctly, they win double the amount of points. However, if the player guesses incorrectly, they lose double the amount of points.
|
||||
|
||||
## Code Organization
|
||||
The code is organized between three Svelte files:
|
||||
- App.svelte
|
||||
- Deck.svelte
|
||||
- HiLo.svelte
|
||||
|
||||
### App.svelte
|
||||
The main purpose of App.svelte is the visual aspect of the game, what the user sees. App.svelte mainly interacts with [HiLo.svelte](#hilosvelte) through "receiving" events, changing the visual elements according to what the user is doing in the game. This is the svelte file that uses p5js, and as such, its main function is the sketch function.
|
||||
On preload, the game is prepared using the HiLo.svelte file, which loads all the images of the game and prepares all the variables that are used throughout the game. On setup, the canvas is created and a "backImage" variable is declared to setup for the image representing the deck (which can be changed based on the Reveal Suit power, since the backImage changes to correspond to the suit of the top card when that power is used). Finally, in the draw function, depending on the "Screen Mode", a game Screen is drawn.
|
||||
There are three Screen Modes (which is managed in [HiLo.svelte](#hilosvelte), because it denotes a state in the game): the Main Screen, the Remaining Screen and the Final Screen. The Main Screen is the basic setup. It shows the score, the number of remaining cards in the deck, and two card images: one representing the Current Card and the other representing the deck. The Remaining Screen shows an overview of all the cards that are remaining in the deck (not in the order that they are in the deck, of course). The Remaining Screen is used for the "See Remaining" power. Finally, the Final Screen is used when the number of remaining cards reaches 0. In this case, the final score is displayed.
|
||||
The other functions in this file are simple visual effects or they interact with the main sketch function (usually, when a power is used).
|
||||
|
||||
### Deck.svelte
|
||||
The main purpose of Deck.svelte is to manage the "deck", as the name implies. It only interacts with [HiLo.svelte](#hilosvelte), as the deck should only be used by the file that controls the logic of the game.
|
||||
This file declares the Card class, which has five attributes (which all have getter functions):
|
||||
- Value
|
||||
- The value of the card, shown in the number displayed on the card. If the card is an Ace, the value is 1. If the card is a Jack, Queen or King, then the value is 11, 12 and 13, respectively.
|
||||
- Suit
|
||||
- The suit of the card, which can be either "Hearts", "Diamonds", "Spades" or "Clubs".
|
||||
- Type
|
||||
- The "type" of the card, which can be either "Ace", "Number" or "Face".
|
||||
- Image
|
||||
- The p5 preloaded image that corresponds to the card.
|
||||
- Id
|
||||
- The card's "id", unique to each card. This is mainly used for the "See Remaining" power, to know the exact "position" of the card.
|
||||
|
||||
Having a Card class ensures to me that it is impossible for any card element to be structured differently.
|
||||
The main function of the file is the createDeck function which, as the name implies, initializes all 52 cards and places them in a global array called deck. In a way, this is a factory of the Card class. The function is simple. It simply goes over every possible value that a card can be (from Ace, 2 - 10, Jack, Queen, King), changes the "suit" and repeats the process for every suit. There are some more things involved (such as getting the image of the card), but the main gist is there.
|
||||
|
||||
Another important function is the shuffleDeck function which shuffles the deck. This uses the "Fisher-Yates Shuffle". The code is basically extracted from a StackOverflow page (https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array) (see more details in [Acknowledgements](#acknowledgements))
|
||||
|
||||
The rest of the functions in Deck.svelte are glorified getters, the most important being the drawCard function, that pops a card from the global deck array.
|
||||
|
||||
### HiLo.svelte
|
||||
Finally, HiLo.svelte is the logical component of the game. All background processes not linked to much visual is handled in HiLo.svelte (with a few exceptions being the buttons, which are in the HiLo.svelte instead of App.svelte despite being a visual element). Since the file is all about the logic, HiLo.svelte mainly interacts with [Deck.svelte](#decksvelte), since that file controls the features of the deck. HiLo.svelte indirectly interacts with [App.svelte](#appsvelte) by sending events to it.
|
||||
The following are the main functions:
|
||||
- initiateGame(p5)
|
||||
- initiateGame is a function that initializes all the values and global variables in HiLo.svelte. It takes a parameter, which is the p5js library element, which is needed to load all the necessary card images.
|
||||
- nextRound(highChosen)
|
||||
- nextRound is a function that is called after the player has pressed the "High" or "Low" buttons. If they pressed "High", highChosen is true and if they pressed "Low", highChosen is false. This function checks if the player's guess is correct or not, adds up the score based on correctness, and replaced the currentCard with the top card of the deck.
|
||||
|
||||
The code also manages the functions that are called when a power is used. All powers have a limited number of uses each, so each function checks if the power can be used. All of these also "dispatch" an event for [App.svelte](#appsvelte) to pick up.
|
||||
- doubleDown
|
||||
- This function changes a multiplier to 2, which is used in scoring in the nextRound function.
|
||||
- revealSuit
|
||||
- This function passes the corresponding suit's "backImage" to [App.svelte](#appsvelte) through an event to be displayed to the player.
|
||||
- seeRemaining
|
||||
- This function changes the Screen Mode. If the current Screen Mode is the Main Screen, the function changes the mode to the Remaining Screen; and if the Remaining Screen is the current Screen Mode, then the Screen Mode to the Main Screen.
|
||||
- seeTheFuture
|
||||
- This function passes the three cards below the top cards to [App.svelte](#appsvelte) through an event, for it to display it the player.
|
||||
|
||||
## Issues
|
||||
My main issue had to do with the button placements. I tried my best to think of a way for the "High"/"Low" buttons to be at the top of the p5 canvas, while the Power buttons were to the left, but implementing that was a lot harder than I expected. I tried making a separate "PowerButtons.svelte" file, but I would need the power buttons to interact with both App.svelte and HiLo.svelte, but I could not figure out a way to use functions from PowerButtons.svelte in HiLo.svelte without including the buttons in HiLo.svelte (which would result in them being together with the HiLo buttons). I would need to have the PowerButtons and the High/Low buttons to be in separate files.
|
||||
I certainly believe it is possible, but, frankly speaking, I don't have much time to make it possible. As such, I will leave the buttons to be like that.
|
||||
|
||||
Another issue was not being able to figure out a way to make the game "more advanced". I find it nice to have such a simple game as a project, but I am sure that other people's projects will be even more amazing. I struggled A LOT, even for something as simple as this, so I'm actually quite glad I went with a simple game.
|
||||
|
||||
## Acknowledgements
|
||||
I used many different resources for this project.
|
||||
|
||||
For help with p5, I used the documentation: https://p5js.org/reference/
|
||||
|
||||
For help with css, I used a small cheat. Recently, through a different course, I learned to use Figma, which is used for making digital prototypes and such; and I learned that I could make an element in Figma and copy it as CSS code. So, to make the buttons, I used Figma: https://www.figma.com
|
||||
Though, before I learned that, I used the following resource to know how to put the HTML buttons to the left of the p5 canvas: https://stackoverflow.com/questions/4550013/html-element-display-in-horizontal-line
|
||||
|
||||
It was very difficult to use Svelte for a game project. It felt very limiting compared to my experience with Unity. The following page helped me understand how to use other Svelte file's functions from outside that Svelte file: https://joyofcode.xyz/using-functions-from-another-svelte-component
|
||||
Amd the following page helped me understand how to create a custom event (though I have a feeling we learned a different way in class, though I might also be wrong): https://stackoverflow.com/questions/66886318/how-do-i-create-a-custom-event-in-svelte
|
||||
|
||||
As said before, I didn't know how to shuffle an array, so I basically almost copy-pasted the Fisher-Yates shuffling algorithm from the following page: https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
|
||||
(I don't think this shoud be a case for plagirism, as it seems to be a well-known algorithm, and I couldn't find another way that worked quite as nicely).
|
||||
I had tried using the following resource, using Array.sort and Math.random, but it became obvious to me that it wasn't actually random, and every permutation wasn't equally likely: https://www.geeksforgeeks.org/how-to-shuffle-an-array-using-javascript/
|
||||
|
||||
Finally, for the See The Future power, I used the Array.slice() function, which I had to look up really quick: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
|
150
src/App.svelte
150
src/App.svelte
|
@ -1,31 +1,155 @@
|
|||
<Deck bind:this={deckComponent}></Deck>
|
||||
|
||||
<script>
|
||||
import P5 from 'p5-svelte';
|
||||
import Deck from './lib/Deck.svelte';
|
||||
import HiLo from './lib/HiLo.svelte';
|
||||
|
||||
let deckComponent;
|
||||
|
||||
const width = 1000;
|
||||
const height = 700;
|
||||
const windowRatio = 0.9;
|
||||
const imageRatio = 3 / 2;
|
||||
const width = window.innerWidth * windowRatio * 0.85;
|
||||
const height = window.innerHeight * windowRatio;
|
||||
const cardImageWidth = height * 0.6;
|
||||
const cardImageHeight = cardImageWidth * imageRatio;
|
||||
const smallCardImageWidth = cardImageWidth * 0.2;
|
||||
const smallCardImageHeight = smallCardImageWidth * imageRatio;
|
||||
const canvasXMargin = height * 0.03;
|
||||
const canvasYMargin = height * 0.02;
|
||||
const textSize = width * 0.025;
|
||||
const blackBackground = [0, 0, 0];
|
||||
const greenBackground = [10, 150, 10];
|
||||
const redBackground = [150, 10, 10];
|
||||
const animationInterval = 300;
|
||||
let outP5;
|
||||
let backImage;
|
||||
|
||||
let gameLogic;
|
||||
|
||||
const sketch = (p5) => {
|
||||
p5.preload = function() {
|
||||
deckComponent.loadDeckImages(p5);
|
||||
outP5 = p5;
|
||||
gameLogic.initiateGame(p5);
|
||||
}
|
||||
p5.setup = function(){
|
||||
p5.createCanvas(width, height);
|
||||
p5.background(100);
|
||||
};
|
||||
p5.draw = function(){
|
||||
//
|
||||
p5.background(blackBackground);
|
||||
backImage = gameLogic.getBackCardImage();
|
||||
};
|
||||
p5.draw = function() {
|
||||
p5.fill(255);
|
||||
p5.textSize(textSize)
|
||||
p5.textAlign(p5.TOP, p5.TOP);
|
||||
|
||||
switch(gameLogic.getScreenMode())
|
||||
{
|
||||
case gameLogic.MAIN_SCREEN:
|
||||
drawMainScreen(p5)
|
||||
break;
|
||||
case gameLogic.REMAINING_SCREEN:
|
||||
drawRemainingScreen(p5);
|
||||
break;
|
||||
default:
|
||||
drawFinalScreen(p5);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
p5.text("Score: " + gameLogic.getScore(), canvasXMargin, (canvasYMargin *2) + cardImageHeight);
|
||||
p5.fill(220);
|
||||
p5.textSize(textSize * 0.8);
|
||||
p5.text(gameLogic.getScoreMessage(), canvasXMargin * 8.6, (canvasYMargin *2) + cardImageHeight);
|
||||
|
||||
p5.text("Remaining: " + gameLogic.getRemaining() + "/52", (canvasXMargin * 2) + cardImageWidth, (canvasYMargin *2) + cardImageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
function drawMainScreen(p5)
|
||||
{
|
||||
p5.image(gameLogic.getCurrentCard().getImage(), canvasXMargin, canvasYMargin, cardImageWidth, cardImageHeight);
|
||||
p5.image(backImage, (canvasXMargin * 2) + cardImageWidth, canvasYMargin, cardImageWidth, cardImageHeight);
|
||||
}
|
||||
|
||||
function drawRemainingScreen(p5)
|
||||
{
|
||||
p5.background(blackBackground);
|
||||
const visibleDeckImages = gameLogic.seeRemainingCardsImages();
|
||||
visibleDeckImages.forEach((cardImage, index) => {
|
||||
p5.image(cardImage, (canvasXMargin * 0.3) + (smallCardImageWidth * (index % 13) * 1.1), (canvasYMargin) + (smallCardImageHeight * Math.floor(index / 13) * 1.1), smallCardImageWidth, smallCardImageHeight);
|
||||
});
|
||||
}
|
||||
|
||||
function drawFinalScreen(p5)
|
||||
{
|
||||
console.log(gameLogic.getScreenMode());
|
||||
p5.background(blackBackground);
|
||||
p5.textSize(36);
|
||||
p5.textAlign(p5.CENTER, p5.CENTER);
|
||||
p5.text("Score: " + gameLogic.getScore(), p5.width/2, p5.height/2);
|
||||
}
|
||||
|
||||
const backgroundAnimation = (event) => {
|
||||
const recentScore = event.detail.recentScore;
|
||||
backImage = gameLogic.getBackCardImage();
|
||||
|
||||
let background;
|
||||
|
||||
if(recentScore > 0) {
|
||||
background = greenBackground;
|
||||
}
|
||||
else if(recentScore < 0){
|
||||
background = redBackground;
|
||||
}
|
||||
else {
|
||||
background = blackBackground;
|
||||
}
|
||||
|
||||
outP5.background(background);
|
||||
|
||||
let animationMultiplier = 2;
|
||||
|
||||
for(let i = 1; i > 0; i -= 0.1) {
|
||||
setTimeout(function() {
|
||||
outP5.background(background.map((n) => n * i))
|
||||
}, animationInterval * (1 - i) * animationMultiplier)
|
||||
}
|
||||
};
|
||||
|
||||
const doubleDownEffect = (event) => {
|
||||
//
|
||||
};
|
||||
|
||||
const revealSuitEffect = (event) => {
|
||||
outP5.background(blackBackground);
|
||||
backImage = event.detail.backImage;
|
||||
}
|
||||
|
||||
const seeRemainingEffect = (event) => {
|
||||
outP5.background(blackBackground);
|
||||
};
|
||||
|
||||
const seeTheFutureEffect = (event) => {
|
||||
outP5.background(blackBackground);
|
||||
gameLogic.getFutureCards().reverse().forEach((card, i) => {
|
||||
const scalingFactor = 1.5;
|
||||
outP5.image(card.getImage(), (canvasXMargin * 3) + (cardImageWidth * 2), canvasYMargin + (smallCardImageHeight * i * scalingFactor * 1.1), smallCardImageWidth * scalingFactor, smallCardImageHeight * scalingFactor);
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<P5 sketch={sketch}></P5>
|
||||
<div class="main_screen">
|
||||
<div class="sub_screen">
|
||||
<HiLo bind:this={gameLogic}
|
||||
on:scoredPoints={backgroundAnimation}
|
||||
on:doublePressed={doubleDownEffect}
|
||||
on:suitPressed={revealSuitEffect}
|
||||
on:remainingPressed={seeRemainingEffect}
|
||||
on:futurePressed={seeTheFutureEffect}>
|
||||
</HiLo>
|
||||
</div>
|
||||
<P5 sketch={sketch}></P5>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.main_screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
|
BIN
src/assets/Blank.png
Normal file
BIN
src/assets/Blank.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -1,7 +1,56 @@
|
|||
<script>
|
||||
// import P5 from 'p5-svelte';
|
||||
|
||||
export let images = {};
|
||||
let images = {};
|
||||
let deck = [];
|
||||
let fullDeck = [];
|
||||
|
||||
export const NUMBER = "Number";
|
||||
export const FACE = "Face";
|
||||
export const ACE = "Ace";
|
||||
export const HEARTS = "Hearts";
|
||||
export const DIAMONDS = "Diamonds";
|
||||
export const SPADES = "Spades";
|
||||
export const CLUBS = "Clubs";
|
||||
|
||||
class Card {
|
||||
value;
|
||||
suit;
|
||||
type;
|
||||
image;
|
||||
id;
|
||||
|
||||
constructor(value, suit, type, image, id)
|
||||
{
|
||||
this.value = value;
|
||||
this.suit = suit;
|
||||
this.type = type;
|
||||
this.image = image;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
getSuit() {
|
||||
return this.suit;
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
getImage()
|
||||
{
|
||||
return this.image;
|
||||
}
|
||||
|
||||
getId()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
export function loadDeckImages(p5)
|
||||
{
|
||||
|
@ -18,81 +67,131 @@
|
|||
}
|
||||
|
||||
images["Back"] = p5.loadImage("./src/assets/Back.png");
|
||||
}
|
||||
|
||||
export const helloWorld = () => {
|
||||
console.log("Hi");
|
||||
}
|
||||
|
||||
let deck = [];
|
||||
|
||||
class Card {
|
||||
value;
|
||||
suit;
|
||||
type;
|
||||
|
||||
constructor(value, suit, type)
|
||||
{
|
||||
this.value = value;
|
||||
this.suit = suit;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
getSuit() {
|
||||
return this.suit;
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.type;
|
||||
}
|
||||
images["Blank"] = p5.loadImage("./src/assets/Blank.png");
|
||||
}
|
||||
|
||||
export function createDeck()
|
||||
{
|
||||
let indexSuit = "Hearts"
|
||||
let indexVal = 1;
|
||||
let indexSuit = HEARTS
|
||||
let indexVal = 0;
|
||||
function switchSuit()
|
||||
{
|
||||
switch(indexSuit) {
|
||||
case "Hearts":
|
||||
indexSuit = "Diamonds";
|
||||
case HEARTS:
|
||||
indexSuit = DIAMONDS;
|
||||
break;
|
||||
case "Diamonds":
|
||||
indexSuit = "Clubs";
|
||||
case DIAMONDS:
|
||||
indexSuit = CLUBS;
|
||||
break;
|
||||
case "Clubs":
|
||||
indexSuit = "Spades";
|
||||
case CLUBS:
|
||||
indexSuit = SPADES;
|
||||
break;
|
||||
case "Spades":
|
||||
indexSuit = "Hearts";
|
||||
case SPADES:
|
||||
indexSuit = HEARTS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const placeholder = new Array(52).fill(0);
|
||||
deck = placeholder.map((obj) => {
|
||||
const val = indexVal;
|
||||
fullDeck = placeholder.map((obj) => {
|
||||
const val = (indexVal % 13) + 1;
|
||||
const suit = indexSuit;
|
||||
|
||||
let type;
|
||||
if(indexVal == 1) type = "Ace";
|
||||
else if(indexVal <= 10) type = "Number";
|
||||
else type = "Face";
|
||||
let nameString = suit;
|
||||
|
||||
indexVal += 1;
|
||||
if(indexVal > 13){
|
||||
indexVal = 1;
|
||||
switchSuit();
|
||||
let type;
|
||||
if(indexVal % 13 == 0){
|
||||
type = ACE;
|
||||
nameString += type;
|
||||
}
|
||||
else if(indexVal % 13 < 10)
|
||||
{
|
||||
type = NUMBER;
|
||||
nameString += (indexVal % 13) + 1
|
||||
}
|
||||
else {
|
||||
type = FACE;
|
||||
switch(indexVal % 13) {
|
||||
default:
|
||||
nameString += "Back";
|
||||
break;
|
||||
case 10:
|
||||
nameString += "Jack";
|
||||
break;
|
||||
case 11:
|
||||
nameString += "Queen";
|
||||
break;
|
||||
case 12:
|
||||
nameString += "King";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Card(val, suit, type)
|
||||
indexVal += 1;
|
||||
if(indexVal % 13 == 0){
|
||||
switchSuit();
|
||||
}
|
||||
|
||||
return new Card(val, suit, type, images[nameString], indexVal);
|
||||
});
|
||||
|
||||
return deck;
|
||||
deck = [...fullDeck];
|
||||
//return deck;
|
||||
}
|
||||
|
||||
export function shuffleDeck()
|
||||
{
|
||||
let currentIndex = deck.length;
|
||||
|
||||
while(currentIndex != 0) {
|
||||
let randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
[deck[currentIndex], deck[randomIndex]] = [deck[randomIndex], deck[currentIndex]];
|
||||
}
|
||||
}
|
||||
|
||||
export function drawCard()
|
||||
{
|
||||
return deck.pop();
|
||||
}
|
||||
|
||||
export function getRemaining()
|
||||
{
|
||||
return deck.length;
|
||||
}
|
||||
|
||||
export function getBackCardImage()
|
||||
{
|
||||
return images["Back"];
|
||||
}
|
||||
|
||||
export function getSpecificImage(imageName)
|
||||
{
|
||||
return images[imageName];
|
||||
}
|
||||
|
||||
export function getTopCardSuit()
|
||||
{
|
||||
return deck[deck.length - 1].getSuit();
|
||||
}
|
||||
|
||||
export function seeRemainingCardsImages()
|
||||
{
|
||||
const boolDeck = new Array(52).fill(false);
|
||||
deck.forEach((card) => {
|
||||
boolDeck[card.getId()] = true
|
||||
});
|
||||
return fullDeck.map((card, i) => {
|
||||
//console.log(card.getImage());
|
||||
if(boolDeck[i]) return card.getImage();
|
||||
else return images["Blank"];
|
||||
});
|
||||
}
|
||||
|
||||
export function getFutureCards()
|
||||
{
|
||||
return deck.slice(-4, -1);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
349
src/lib/HiLo.svelte
Normal file
349
src/lib/HiLo.svelte
Normal file
|
@ -0,0 +1,349 @@
|
|||
<Deck bind:this={deck}></Deck>
|
||||
|
||||
<script>
|
||||
import Deck from './Deck.svelte';
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export const MAIN_SCREEN = "MainScreen";
|
||||
export const REMAINING_SCREEN = "RemainingScreen";
|
||||
const FINAL_SCREEN = "FinalScreen";
|
||||
export let screenMode = MAIN_SCREEN;
|
||||
|
||||
let doubleButton;
|
||||
let suitButton;
|
||||
let remainingButton;
|
||||
let futureButton;
|
||||
|
||||
// Powers
|
||||
const doubleUsesMax = 3;
|
||||
const suitUsesMax = 10;
|
||||
const remainingUsesMax = 3;
|
||||
const futureUsesMax = 2;
|
||||
let doubleUses = doubleUsesMax;
|
||||
let suitUses = suitUsesMax;
|
||||
let remainingUses = remainingUsesMax;
|
||||
let futureUses = futureUsesMax;
|
||||
let doubleUsed = false;
|
||||
let futureUsed = false;
|
||||
let suitUsed = false;
|
||||
|
||||
let currentCard;
|
||||
let deck;
|
||||
let powers;
|
||||
let score = 0;
|
||||
let multiplier = 1;
|
||||
let recentScore = 0;
|
||||
let gameEnd = false;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export function initiateGame(p5)
|
||||
{
|
||||
deck.loadDeckImages(p5);
|
||||
startGame();
|
||||
}
|
||||
|
||||
export function startGame()
|
||||
{
|
||||
deck.createDeck();
|
||||
deck.shuffleDeck();
|
||||
gameEnd = false;
|
||||
currentCard = deck.drawCard();
|
||||
score = currentCard.getValue();
|
||||
multiplier = 1;
|
||||
recentScore = 0;
|
||||
|
||||
screenMode = MAIN_SCREEN;
|
||||
doubleUses = doubleUsesMax;
|
||||
suitUses = suitUsesMax;
|
||||
remainingUses = remainingUsesMax;
|
||||
futureUses = futureUsesMax;
|
||||
doubleUsed = false;
|
||||
futureUsed = false;
|
||||
suitUsed = false;
|
||||
doubleButton.innerHTML = "Double Down x" + doubleUses;
|
||||
suitButton.innerHTML = "Reveal Suit x" + suitUses;
|
||||
remainingButton.innerHTML = "See Remaining x" + remainingUses;
|
||||
futureButton.innerHTML = "See The Future x" + futureUses;
|
||||
}
|
||||
|
||||
export function nextRound(highChosen)
|
||||
{
|
||||
if(gameEnd) return;
|
||||
if(screenMode == REMAINING_SCREEN)
|
||||
{
|
||||
changeScreenMode(MAIN_SCREEN);
|
||||
return;
|
||||
}
|
||||
|
||||
const topCard = deck.drawCard();
|
||||
|
||||
const currentValue = currentCard.getValue();
|
||||
const topValue = topCard.getValue();
|
||||
|
||||
|
||||
const currentHigherThanTop = evaluateIfCardHigherThanCard(currentCard, topCard);
|
||||
const equalToCurrent = currentValue == topValue;
|
||||
|
||||
if(topValue == 1)
|
||||
{
|
||||
if(highChosen) recentScore = 15;
|
||||
else recentScore = 1;
|
||||
}
|
||||
else if((highChosen && !currentHigherThanTop) || (!highChosen && currentHigherThanTop) || equalToCurrent)
|
||||
{
|
||||
recentScore = topValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
recentScore = -(15 - topValue);
|
||||
}
|
||||
|
||||
recentScore *= multiplier;
|
||||
|
||||
multiplier = 1;
|
||||
doubleUsed = false;
|
||||
suitUsed = false;
|
||||
futureUsed = false;
|
||||
|
||||
score += recentScore
|
||||
|
||||
currentCard = topCard;
|
||||
|
||||
dispatch("scoredPoints", { recentScore })
|
||||
if(deck.getRemaining() == 0) {
|
||||
changeScreenMode(FINAL_SCREEN);
|
||||
gameEnd = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const pickHigh = () => nextRound(true);
|
||||
const pickLow = () => nextRound(false);
|
||||
|
||||
function evaluateIfCardHigherThanCard(firstCard, secondCard)
|
||||
{
|
||||
if(firstCard.getType() == deck.ACE)
|
||||
{
|
||||
const secondType = secondCard.getType();
|
||||
if(secondType == deck.NUMBER) return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return firstCard.getValue() > secondCard.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
export function changeScreenMode(mode)
|
||||
{
|
||||
screenMode = mode;
|
||||
switch(mode)
|
||||
{
|
||||
case MAIN_SCREEN:
|
||||
remainingButton.innerHTML = "See Remaining x" + remainingUses;
|
||||
break;
|
||||
case REMAINING_SCREEN:
|
||||
remainingButton.innerHTML = "Go Back";
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Powers
|
||||
function doubleDown()
|
||||
{
|
||||
if(gameEnd) return;
|
||||
changeScreenMode(MAIN_SCREEN);
|
||||
if(doubleUses > 0 && !doubleUsed) {
|
||||
doubleUses -= 1;
|
||||
doubleUsed = true;
|
||||
multiplier = 2;
|
||||
doubleButton.innerHTML = "Double Down x" + doubleUses;
|
||||
dispatch("doublePressed");
|
||||
}
|
||||
}
|
||||
|
||||
function revealSuit()
|
||||
{
|
||||
if(gameEnd) return;
|
||||
changeScreenMode(MAIN_SCREEN);
|
||||
if(suitUses > 0 && !suitUsed) {
|
||||
suitUses -= 1;
|
||||
suitUsed = true;
|
||||
const suit = deck.getTopCardSuit();
|
||||
const backImage = deck.getSpecificImage(suit + "Back");
|
||||
|
||||
suitButton.innerHTML = "Reveal Suit x" + suitUses;
|
||||
dispatch("suitPressed", { backImage });
|
||||
}
|
||||
}
|
||||
|
||||
function seeRemaining()
|
||||
{
|
||||
if(gameEnd) return;
|
||||
if(screenMode == MAIN_SCREEN)
|
||||
{
|
||||
if(remainingUses > 0) {
|
||||
remainingUses -= 1;
|
||||
changeScreenMode(REMAINING_SCREEN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
changeScreenMode(MAIN_SCREEN);
|
||||
}
|
||||
dispatch("remainingPressed");
|
||||
}
|
||||
|
||||
function seeTheFuture()
|
||||
{
|
||||
if(gameEnd) return;
|
||||
changeScreenMode(MAIN_SCREEN);
|
||||
if(futureUses > 0 && !futureUsed) {
|
||||
futureUses -= 1;
|
||||
futureUsed = true;
|
||||
futureButton.innerHTML = "See The Future x" + futureUses;
|
||||
dispatch("futurePressed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Getters
|
||||
export function getCurrentCard()
|
||||
{
|
||||
return currentCard;
|
||||
}
|
||||
|
||||
export function getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
export function getScoreMessage() {
|
||||
let message = ""
|
||||
if(recentScore > 0) {
|
||||
message = "+"
|
||||
}
|
||||
else if(recentScore == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return message + recentScore;
|
||||
}
|
||||
|
||||
export function getRecentScore() {
|
||||
return recentScore;
|
||||
}
|
||||
|
||||
export function getBackCardImage()
|
||||
{
|
||||
return deck.getBackCardImage();
|
||||
}
|
||||
|
||||
export function getScreenMode()
|
||||
{
|
||||
return screenMode;
|
||||
}
|
||||
|
||||
export function getRemaining() {
|
||||
return deck.getRemaining();
|
||||
}
|
||||
|
||||
export function getMultiplier()
|
||||
{
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
export function seeRemainingCardsImages() {
|
||||
return deck.seeRemainingCardsImages();
|
||||
}
|
||||
|
||||
export function getFutureCards()
|
||||
{
|
||||
return deck.getFutureCards();
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<button class="Powers" bind:this={doubleButton} on:click={doubleDown}>Double Down x3</button>
|
||||
<div class="labelText">Next guess will be worth double.</div>
|
||||
<button class="Powers" bind:this={suitButton} on:click={revealSuit}>Reveal Suit x10</button>
|
||||
<div class="labelText">See the top card's suit.</div>
|
||||
<button class="Powers" bind:this={remainingButton} on:click={seeRemaining}>See Remaining x3</button>
|
||||
<div class="labelText">Get overview of the remaining cards of the deck.</div>
|
||||
<button class="Powers" bind:this={futureButton} on:click={seeTheFuture}>See The Future x2</button>
|
||||
<div class="labelText">See three cards from the deck that come after the top card.</div>
|
||||
<button class="MainButtons" on:click={pickHigh}>High</button>
|
||||
<button class="MainButtons" on:click={pickLow}>Low</button>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
button {
|
||||
/* Frame 1 */
|
||||
box-sizing: border-box;
|
||||
|
||||
/* Auto layout */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* padding: 6px 0px;
|
||||
gap: 10px; */
|
||||
|
||||
position: relative;
|
||||
width: 9em; /*105;*/
|
||||
height: 5em; /*54*/
|
||||
|
||||
background: #55109A;
|
||||
border: 2px solid #000000;
|
||||
|
||||
|
||||
/* button 1 */
|
||||
/* width: 68px;
|
||||
height: 17px; */
|
||||
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
/* identical to box height */
|
||||
text-align: center;
|
||||
color: #FFFFFF;
|
||||
|
||||
|
||||
/* Inside auto layout */
|
||||
flex: none;
|
||||
order: 0;
|
||||
flex-grow: 0;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.Powers {
|
||||
height: 3em; /*54*/
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.labelText {
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0px 0px;
|
||||
gap: 10px;
|
||||
|
||||
position: relative;
|
||||
width: 8em; /*105;*/
|
||||
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
/* identical to box height */
|
||||
text-align: center;
|
||||
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user