5 |
6 | # Uno
7 |
8 | This is the card game Uno. It is based on the original card game and implements most of the original rules. The game can be played against up to 3 computer players. It is a singleplayer game.
9 |
10 | ## Contents
11 |
12 | - [How to Start the Game](#how-to-start-the-game)
13 | - [Development](#development)
14 | - [Credits](#credits)
15 |
16 | ## How to Start the Game
17 |
18 | Visit https://compscilauren.github.io/uno/index.html
19 |
20 | ## Development
21 |
22 | Want to contribute? Great!
23 |
24 | To fix a bug or add an enhancement, follow these steps:
25 |
26 | - Fork the repo
27 | - Create a new branch (`git checkout -b improve-feature`)
28 | - Make the appropriate changes in the files
29 | - Add changes to reflect the changes made
30 | - Commit your changes (`git commit -am 'Improve feature'`)
31 | - Push to the branch (`git push origin improve-feature`)
32 | - Create a Pull Request
33 |
34 | ## Credits
35 |
36 | CSS for uno cards based off [a pen by rn404 on codepen.com](https://codepen.io/rn404/pen/mEpiG) containing CSS for cards 0-10
37 |
--------------------------------------------------------------------------------
/players.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Player constructor
3 | * @param {*} deck
4 | * @param {*} id
5 | * @param {*} index
6 | * @param {*} bot
7 | * @param {*} unoCall
8 | */
9 | function Player(deck, id, index, bot, unoCall) {
10 | this.isBot = bot;
11 | this.playerDeck = deck;
12 | this.playerID = id;
13 | this.playerIndex = index;
14 | this.playerUnoCall = unoCall;
15 | this.botLogic = function () {
16 | let numBotCards = this.playerDeck.amtCards;
17 |
18 | // bot behavior
19 | for (let i = 0; i < numBotCards; i++) {
20 | if (players[gameTurn].playerDeck.isValid(i)) {
21 | if (players[gameTurn].playerDeck.amtCards == 2) {
22 | players[gameTurn].unoCall = true;
23 | }
24 | players[gameTurn].playerDeck.playCard(i);
25 | return;
26 | }
27 | }
28 |
29 | if (drawStack.stackAmt != 0) {
30 | drawACard();
31 | } else {
32 | // draw a card and check if it is a match. Will break loop if hits 20 card limit (prevents infinite decks)
33 | while (!this.playerDeck.playCard(this.playerDeck.amtCards - 1)) {
34 | drawACard();
35 | }
36 | }
37 | };
38 | }
39 |
40 | /**
41 | * End current player's turn and begin next player's turn
42 | */
43 | function rotatePlayers() {
44 | gameTurn = gameTurn + gameDirection;
45 |
46 | if (gameTurn == players.length) {
47 | gameTurn = 0;
48 | }
49 | else if (gameTurn < 0) {
50 | gameTurn = players.length - 1;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/rules.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rules
6 |
7 |
9 |
12 |
13 |
14 |
15 |
16 |
Rule and Information
17 |
18 |
19 |
20 |
This is a modified version of the game of Uno.
21 |
22 |
23 | You must call uno before playing your second-to-last card. Penalty for not doing this is to draw 2 cards.
24 |
25 |
26 |
27 | If you do not have a valid card to play you must draw until you get one you can play.
28 |
29 |
30 |
31 | The draw-4 cards can be stacked. If a player plays a draw-4, the next player must play a
32 | draw-4 or else draw the total number of cards from the deck. This same rule applies to the draw-2 card.
33 |
34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/special-cards.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Reverse the direction of player rotation
3 | */
4 | function cardReverse() {
5 | if (players.length == 2) {
6 | rotatePlayers();
7 | } else {
8 | gameDirection = -1 * gameDirection;
9 | }
10 | }
11 |
12 | /**
13 | * Skip the next player in rotation
14 | */
15 | function cardSkip() {
16 | rotatePlayers();
17 | }
18 |
19 | function cardWild() {
20 | if (players[gameTurn].isBot) {
21 | let colorArray = ["Red", "Green", "Blue", "Yellow"];
22 | let randColor = colorArray[Math.floor(Math.random() * colorArray.length)];
23 | discardPile.cards[discardPile.cards.length - 1].color = randColor;
24 | let colorChoice = convertColorToHex(randColor);
25 | $(".chosen-wild-card-color .inner").css("background", colorChoice);
26 | } else {
27 | document.getElementById("overlay").style.display = "block";
28 | }
29 | return true;
30 | }
31 |
32 | function selectWildColor(color) {
33 | discardPile.cards[
34 | discardPile.cards.length - 1
35 | ].color = color;
36 | $(".chosen-wild-card-color .inner").css("background", convertColorToHex(color));
37 | isColorSelected = true;
38 | rotatePlayers();
39 | play();
40 | document.getElementById("overlay").style.display = "none";
41 | }
42 |
43 | function cardDraw2() {
44 | drawStack.stackAmt++;
45 | drawStack.cardType = 2;
46 | drawStack.cardValue = 10;
47 | drawStack.updateStack();
48 | }
49 |
50 | function cardDraw4() {
51 | drawStack.stackAmt++;
52 | drawStack.cardType = 4;
53 | drawStack.cardValue = 1;
54 | drawStack.updateStack();
55 | cardWild();
56 | }
57 |
58 | function convertColorToHex(color) {
59 | switch (color) {
60 | case "Red":
61 | return "#c72a18";
62 | case "Green":
63 | return "#18a849";
64 | case "Blue":
65 | return "#0063b3";
66 | case "Yellow":
67 | return "#e6ca1e";
68 | }
69 | }
--------------------------------------------------------------------------------
/documentation/styles/prettify-jsdoc.css:
--------------------------------------------------------------------------------
1 | /* JSDoc prettify.js theme */
2 |
3 | /* plain text */
4 | .pln {
5 | color: #000000;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | /* string content */
11 | .str {
12 | color: #006400;
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
17 | /* a keyword */
18 | .kwd {
19 | color: #000000;
20 | font-weight: bold;
21 | font-style: normal;
22 | }
23 |
24 | /* a comment */
25 | .com {
26 | font-weight: normal;
27 | font-style: italic;
28 | }
29 |
30 | /* a type name */
31 | .typ {
32 | color: #000000;
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | /* a literal value */
38 | .lit {
39 | color: #006400;
40 | font-weight: normal;
41 | font-style: normal;
42 | }
43 |
44 | /* punctuation */
45 | .pun {
46 | color: #000000;
47 | font-weight: bold;
48 | font-style: normal;
49 | }
50 |
51 | /* lisp open bracket */
52 | .opn {
53 | color: #000000;
54 | font-weight: bold;
55 | font-style: normal;
56 | }
57 |
58 | /* lisp close bracket */
59 | .clo {
60 | color: #000000;
61 | font-weight: bold;
62 | font-style: normal;
63 | }
64 |
65 | /* a markup tag name */
66 | .tag {
67 | color: #006400;
68 | font-weight: normal;
69 | font-style: normal;
70 | }
71 |
72 | /* a markup attribute name */
73 | .atn {
74 | color: #006400;
75 | font-weight: normal;
76 | font-style: normal;
77 | }
78 |
79 | /* a markup attribute value */
80 | .atv {
81 | color: #006400;
82 | font-weight: normal;
83 | font-style: normal;
84 | }
85 |
86 | /* a declaration */
87 | .dec {
88 | color: #000000;
89 | font-weight: bold;
90 | font-style: normal;
91 | }
92 |
93 | /* a variable name */
94 | .var {
95 | color: #000000;
96 | font-weight: normal;
97 | font-style: normal;
98 | }
99 |
100 | /* a function name */
101 | .fun {
102 | color: #000000;
103 | font-weight: bold;
104 | font-style: normal;
105 | }
106 |
107 | /* Specify class=linenums on a pre to get line numbering */
108 | ol.linenums {
109 | margin-top: 0;
110 | margin-bottom: 0;
111 | }
112 |
--------------------------------------------------------------------------------
/cheats.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Lists cheat functions in console
3 | */
4 | function showMeCheats() {
5 | console.log("------------------------------------");
6 | console.log("newPlayfieldCard() -- Add new card to the discard pile");
7 | console.log("");
8 |
9 | console.log('drawSpecificCard("Color", Value) -- Give yourself a specific card');
10 |
11 | console.log("COLOR OPTIONS: Red, Green, Blue, Yellow, Special");
12 | console.log("VALUE OPTIONS for R-G-B-Y: 0-9, 10 (for draw 2), 11 (for reverse), 12 (for skip)");
13 | console.log("VALUE OPTIONS for 'Special': 13 for Wild, 14 for Wild Draw 4");
14 | console.log("");
15 |
16 | console.log("forceAdd(number) -- Give yourself any number of cards");
17 | console.log("");
18 |
19 | console.log("forceRemove(number) -- Remove any number of cards from your hand starting from left side");
20 | console.log("NOTE: Must leave at least 2 cards in hand");
21 | console.log("------------------------------------");
22 | }
23 |
24 | /**
25 | * Gives a new playfield card
26 | */
27 | function newPlayfieldCard() {
28 | initializeWindow();
29 | }
30 |
31 | /**
32 | * Gives player a specific number of cards
33 | */
34 | function forceAdd(numCards) {
35 | if (numCards > 0) {
36 | drawCards(numCards);
37 | } else {
38 | console.log("Invalid number of cards: " + numCards);
39 | }
40 | }
41 |
42 | /**
43 | * Draws a specific number of cards and adds them to player's hand
44 | */
45 | function drawCards(numCards) {
46 | for (let i = 0; i < numCards; i++) {
47 | players[gameTurn].playerDeck.drawCard();
48 | }
49 | }
50 |
51 | /**
52 | * Removes a specific number of cards from players hand
53 | */
54 | function forceRemove(numCards) {
55 | if (numCards > 0) {
56 | removeManyCards(numCards);
57 | } else {
58 | console.log("Invalid number of cards: " + numCards);
59 | }
60 | }
61 |
62 | /**
63 | * Gives player a specific card (input from console)
64 | */
65 | function drawSpecificCard(cardColor, cardValue) {
66 | if ((cardColor == "Special" && cardValue > 1) || cardValue < 0) {
67 | console.log("Invalid wild card selection: " + cardColor + " " + cardValue);
68 | return;
69 | } else if (cardValue > 12) {
70 | console.log("Invalid card selection: " + cardColor + " " + cardValue);
71 | return;
72 | } else {
73 | drawSpecificCard(cardColor, cardValue);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/documentation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JSDoc: Home
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
51 |
52 |
55 |
56 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/cards.js:
--------------------------------------------------------------------------------
1 | /**
2 | * card constructor
3 | * @param {*} color
4 | * @param {*} value
5 | */
6 | function Card(color, value) {
7 | this.color = color;
8 | this.value = value;
9 | this.getColorValue = function () {
10 | switch (this.color) {
11 | case "Red":
12 | return "#A60000";
13 | case "Blue":
14 | return "#0000FF";
15 | case "Green":
16 | return "#004f19";
17 | case "Yellow":
18 | return "#e5bf00";
19 | default:
20 | return "#333333";
21 | }
22 | };
23 | }
24 |
25 | /**
26 | * Function draws a specific card for cheat
27 | */
28 | function drawSpecificCard(cardColor, cardValue) {
29 | players[gameTurn].playerDeck.drawSpecificCard(cardColor, cardValue);
30 | }
31 |
32 | /**
33 | * Function draws a specific card for cheat code
34 | */
35 | function removeManyCards(numberOfCards) {
36 | if (numberOfCards > players[gameTurn].playerDeck.amtCards - 2) {
37 | return;
38 | }
39 |
40 | for (let i = 0; i < numberOfCards; i++) {
41 | players[gameTurn].playerDeck.removeCard(0);
42 | }
43 | players[gameTurn].playerDeck.reloadHand();
44 | }
45 |
46 | /**
47 | * Function draws cards and adds them to playerhand
48 | */
49 | function drawACard() {
50 | if (drawStack.stackAmt != 0) {
51 | let drawTimes = drawStack.cardType * drawStack.stackAmt;
52 | drawStack.clearVisual();
53 | for (let i = 0; i < drawTimes; i++) {
54 | players[gameTurn].playerDeck.drawCard();
55 | }
56 |
57 | drawStack.stackAmt = 0;
58 | rotatePlayers();
59 | play();
60 | } else if (forcePlay()) {
61 | let audio = new Audio("error.mp3");
62 |
63 | audio.play();
64 | } else {
65 | players[gameTurn].playerDeck.drawCard();
66 | }
67 | }
68 |
69 | $(drawCardPile).click(function () {
70 | drawACard();
71 | });
72 |
73 | /**
74 | * Changes the global card object to random color/value assignment
75 | */
76 | function selectPlayfieldCard() {
77 | let colorArray = ["Red", "Green", "Blue", "Yellow"];
78 | let randColor = colorArray[Math.floor(Math.random() * colorArray.length)];
79 | let randValue = Math.floor(Math.random() * 10);
80 | let tempCard = new Card(randColor, randValue);
81 |
82 | discard(tempCard);
83 | }
84 |
85 | function discard(card) {
86 | discardPile.addCard(card);
87 | if (discardPile.cards.length > 5) discardPile.removeCard(0);
88 | }
89 |
90 | function forcePlay() {
91 | for (let i = 0; i < players[gameTurn].playerDeck.cards.length; i++) {
92 | if (players[gameTurn].playerDeck.isValid(i)) return true;
93 | }
94 | return false;
95 | }
96 |
--------------------------------------------------------------------------------
/documentation/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | .form-size {
2 | width: 50%;
3 | margin: auto;
4 | font-size: 2rem;
5 | }
6 |
7 | #drawCardPile {
8 | overflow: visible;
9 | width: 500px;
10 | height: 178px;
11 | margin-left: -192px;
12 | }
13 |
14 | #drawCardPile > .card {
15 | margin-top: -7px;
16 | }
17 |
18 | .drawCardContainer {
19 | display: inline-block;
20 | }
21 |
22 | .activePlayer {
23 | font-weight: bold;
24 | color: #000;
25 | }
26 |
27 | .wildStyle {
28 | height: 100vh;
29 | }
30 |
31 | body {
32 | background: url(retina_wood.png);
33 | }
34 |
35 | .my-card {
36 | position: relative;
37 | top: 0;
38 | transition: top 0.5s ease, margin-right 0.5s, margin-left 0.5s;
39 | transition-delay: 0.1s;
40 | }
41 |
42 | .my-card:hover {
43 | cursor: pointer;
44 | top: -20px;
45 | margin-right: 10px;
46 | margin-left: 10px;
47 | }
48 |
49 | .sideHand > .card {
50 | margin-right: 0;
51 | margin-left: 0;
52 | margin-bottom: -110px;
53 | width: 140px;
54 | height: 100px;
55 | }
56 |
57 | h3 {
58 | font-weight: normal;
59 | color: gray;
60 | }
61 |
62 | li {
63 | margin-bottom: 5px;
64 | }
65 |
66 | .center-card-hands {
67 | display: flex;
68 | flex-direction: column;
69 | justify-content: center;
70 | align-items: center;
71 | }
72 |
73 | .unoButton {
74 | animation-name: unoButtonInvalid;
75 | animation-duration: 0.5s;
76 | }
77 |
78 | #overlay {
79 | position: fixed; /* Sit on top of the page content */
80 | display: none; /* Hidden by default */
81 | width: 100%;
82 | height: 100%;
83 | top: 0;
84 | left: 0;
85 | right: 0;
86 | bottom: 0;
87 | background-color: rgba(0,0,0,0.5);
88 | z-index: 2;
89 | }
90 |
91 | #text{
92 | position: absolute;
93 | top: 50%;
94 | left: 50%;
95 | font-size: 50px;
96 | color: white;
97 | transform: translate(-50%,-50%);
98 | -ms-transform: translate(-50%,-50%);
99 | }
100 |
101 | .main-container {
102 | display: flex;
103 | align-items: center;
104 | flex-wrap: wrap;
105 | }
106 |
107 | .size {
108 | width: 5vw;
109 | height: 5vw;
110 | box-shadow: 2px 2px 3px black;
111 | margin: 2rem;
112 | cursor: pointer; /* Add a pointer on hover */
113 | }
114 |
115 | .red-circle {
116 | background-color: #c72a18;
117 | border-radius: 50%;
118 | border: 2px solid #ffffff;
119 | }
120 |
121 | .blue-circle {
122 | background-color: #0063b3;
123 | border-radius: 50%;
124 | border: 2px solid #ffffff;
125 | }
126 |
127 | .yellow-circle {
128 | background-color: #e6ca1e;
129 | border-radius: 50%;
130 | border: 2px solid #ffffff;
131 | }
132 |
133 | .green-circle {
134 | background-color: #18a849;
135 | border-radius: 50%;
136 | border: 2px solid #ffffff;
137 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at compscilauren@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/animations.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Animates the action of drawing a card from the playfield deck
3 | * @param {*} thisHandId the player's hand
4 | * @param {*} randValue the card value
5 | * @param {*} randColor the card color
6 | * @param {*} thisObject the deck
7 | */
8 | function drawCardAnimation(thisHandId, randValue, randColor, thisObject) {
9 | // obtain drawPile div
10 | let drawPile = document.getElementById("drawCardPile");
11 |
12 | // create containers for the cards and add class
13 | let drawCardContainer = document.createElement("div");
14 | let drawCardContainerBack = document.createElement("div");
15 | drawCardContainer.classList.add("drawCardContainer");
16 | drawCardContainerBack.classList.add("drawCardContainer");
17 |
18 | // append containers to drawpile div
19 | drawPile.append(drawCardContainer);
20 | drawPile.append(drawCardContainerBack);
21 |
22 | // create card visuals
23 | let cardDivBack = document.createElement("div");
24 | let cardDiv = document.createElement("div");
25 |
26 | // append cards into containers
27 | drawCardContainer.append(cardDiv);
28 | drawCardContainerBack.append(cardDivBack);
29 |
30 | //add class card to card divs
31 | cardDiv.classList.add("card");
32 | cardDivBack.classList.add("card");
33 |
34 | // create the inside of the cards
35 | let cardSpan = document.createElement("span");
36 | cardDiv.append(cardSpan);
37 | cardSpan.classList.add("inner");
38 |
39 | let cardSpanInner = document.createElement("span");
40 | cardSpan.append(cardSpanInner);
41 | cardSpanInner.classList.add("mark");
42 |
43 | let cardSpanBack = document.createElement("span");
44 | cardDivBack.append(cardSpanBack);
45 | cardSpanBack.classList.add("inner");
46 |
47 | let cardSpanInnerBack = document.createElement("span");
48 | cardSpanBack.append(cardSpanInnerBack);
49 | cardSpanInnerBack.classList.add("mark");
50 |
51 | cardDiv.append();
52 | cardDivBack.append();
53 |
54 | addCSSDesignToBackOfCard(cardDivBack, cardSpanInnerBack);
55 |
56 | if (thisHandId == "BottomSeat") {
57 | addCSSDesignToCard(cardDiv, cardSpanInner, randValue);
58 |
59 | // determines color of card drawn from the playfield deck
60 | switch (randColor) {
61 | case "Blue":
62 | cardDiv.classList.add("blue");
63 | break;
64 | case "Red":
65 | cardDiv.classList.add("red");
66 | break;
67 | case "Green":
68 | cardDiv.classList.add("green");
69 | break;
70 | case "Yellow":
71 | cardDiv.classList.add("yellow");
72 | break;
73 | }
74 |
75 | drawCardContainer.classList.add("drawCardAnimationFrontDown");
76 | drawCardContainerBack.classList.add("drawCardAnimationBack");
77 | } else {
78 | addCSSDesignToBackOfCard(cardDiv, cardSpanInner);
79 |
80 | if (thisHandId == "TopSeat") {
81 | drawCardContainer.classList.add("drawCardAnimationHiddenUp");
82 | } else if (thisHandId == "RightSeat") {
83 | drawCardContainer.classList.add("drawCardAnimationHiddenRight");
84 | } else if (thisHandId == "LeftSeat") {
85 | drawCardContainer.classList.add("drawCardAnimationHiddenLeft");
86 | } else {
87 | drawCardContainer.classList.add("drawCardAnimationFront");
88 | }
89 | drawCardContainerBack.classList.add("drawCardBackHidden");
90 | }
91 |
92 | setTimeout(function () {
93 | drawPile.removeChild(drawPile.childNodes[0]);
94 | drawPile.removeChild(drawPile.childNodes[0]);
95 | thisObject.reloadHand();
96 | }, 1000);
97 | }
98 |
--------------------------------------------------------------------------------
/animations.css:
--------------------------------------------------------------------------------
1 | @keyframes cardDrawBack {
2 | 0% {
3 | transform: scale(1, 1);
4 | }
5 | 30% {
6 | transform: scale(1.5, 1.5) rotateY(0);
7 | }
8 | 60% {
9 | transform: scale(1.5, 1.5) rotateY(180deg);
10 | }
11 | 100% {
12 | transform: scale(1.5, 1.5) rotateY(180deg);
13 | }
14 | }
15 |
16 | @keyframes cardDrawFront {
17 | 0% {
18 | transform: scale(1, 1) rotateY(180deg);
19 | }
20 | 30% {
21 | transform: scale(1.5, 1.5) rotateY(180deg);
22 | }
23 | 60% {
24 | transform: scale(1.5, 1.5) rotateY(0);
25 | z-index: 2;
26 | bottom: 0;
27 | }
28 | 90% {
29 | transform: scale(1.5, 1.5) rotateY(0);
30 | z-index: 2;
31 | bottom: -150px;
32 | opacity: 1;
33 | }
34 | 100% {
35 | transform: scale(1.5, 1.5) rotateY(0);
36 | z-index: 2;
37 | bottom: -200px;
38 | opacity: 0;
39 | }
40 | }
41 |
42 | @keyframes cardDrawHiddenRight {
43 | 0% {
44 | transform: scale(1, 1);
45 | }
46 | 50% {
47 | transform: scale(1.5, 1.5);
48 | right: 0;
49 | }
50 | 80% {
51 | transform: scale(1.5, 1.5);
52 | right: -120px;
53 | opacity: 1;
54 | }
55 | 100% {
56 | transform: scale(1.5, 1.5);
57 | right: -200px;
58 | opacity: 0;
59 | }
60 | }
61 |
62 | @keyframes cardDrawHiddenLeft {
63 | 0% {
64 | transform: scale(1, 1);
65 | }
66 | 50% {
67 | transform: scale(1.5, 1.5);
68 | left: 0;
69 | }
70 | 80% {
71 | transform: scale(1.5, 1.5);
72 | left: -120px;
73 | opacity: 1;
74 | }
75 | 100% {
76 | transform: scale(1.5, 1.5);
77 | left: -200px;
78 | opacity: 0;
79 | }
80 | }
81 |
82 | @keyframes cardDrawHiddenUp {
83 | 0% {
84 | transform: scale(1, 1);
85 | }
86 | 50% {
87 | transform: scale(1.5, 1.5);
88 | top: 0;
89 | }
90 | 80% {
91 | transform: scale(1.5, 1.5);
92 | top: -120px;
93 | opacity: 1;
94 | }
95 | 100% {
96 | transform: scale(1.5, 1.5);
97 | top: -200px;
98 | opacity: 0;
99 | }
100 | }
101 |
102 | @keyframes unoButtonInvalid {
103 | 0% {
104 | background-color: none;
105 | }
106 | 50% {
107 | background-color: red;
108 | }
109 | 100% {
110 | background-color: none;
111 | }
112 | }
113 |
114 | @keyframes shakeHorizontal {
115 | 0% {
116 | left: 0;
117 | }
118 | 33% {
119 | left: 5px;
120 | }
121 | 66% {
122 | left: -5px;
123 | }
124 | 100% {
125 | left: 0;
126 | }
127 | }
128 |
129 | .drawCardAnimationBack {
130 | z-index: 1;
131 | position: absolute;
132 | backface-visibility: hidden;
133 | -webkit-backface-visibility: hidden;
134 | margin-left: -75px;
135 | animation-name: cardDrawBack;
136 | animation-duration: 1s;
137 | }
138 |
139 | .drawCardBackHidden {
140 | transform: rotateY(180deg);
141 | position: absolute;
142 | backface-visibility: hidden;
143 | -webkit-backface-visibility: hidden;
144 | margin-left: -75px;
145 | }
146 |
147 | .drawCardAnimationHidden {
148 | position: relative;
149 | backface-visibility: hidden;
150 | -webkit-backface-visibility: hidden;
151 | animation-name: cardDrawHidden;
152 | animation-duration: 1.2s;
153 | }
154 |
155 | .drawCardAnimationFrontDown {
156 | transform: rotateY(180deg);
157 | position: relative;
158 | backface-visibility: hidden;
159 | -webkit-backface-visibility: hidden;
160 | animation-name: cardDrawFront;
161 | animation-duration: 1s;
162 | }
163 |
164 | .drawCardAnimationHiddenRight {
165 | z-index: 1;
166 | position: relative;
167 | backface-visibility: hidden;
168 | -webkit-backface-visibility: hidden;
169 | animation-name: cardDrawHiddenRight;
170 | animation-duration: 1.2s;
171 | }
172 |
173 | .drawCardAnimationHiddenLeft {
174 | z-index: 1;
175 | position: relative;
176 | backface-visibility: hidden;
177 | -webkit-backface-visibility: hidden;
178 | animation-name: cardDrawHiddenLeft;
179 | animation-duration: 1.2s;
180 | }
181 |
182 | .drawCardAnimationHiddenUp {
183 | z-index: 1;
184 | position: relative;
185 | backface-visibility: hidden;
186 | -webkit-backface-visibility: hidden;
187 | animation-name: cardDrawHiddenUp;
188 | animation-duration: 1.2s;
189 | }
190 |
191 | .invalid {
192 | animation-name: shakeHorizontal;
193 | animation-duration: 0.1s;
194 | animation-iteration-count: 5;
195 | }
196 |
--------------------------------------------------------------------------------
/testing/testing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Testing
5 |
6 |
7 |
8 |
9 |
10 |
12 |
15 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Testing Suite
30 | Testing Notes:
31 | Please run tests on a freshly opened window. Simply reloading the window will not clear all old data correctly.
32 | Tests output to console. You can filer the console by '$' to view only test-related logs
33 |
34 |
35 |
36 |
37 |
38 |
39 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | *Reminder, you must call Uno BEFORE playing your second-to-last card!
100 |