A simple card-flip memory game with a developer twist: match the programming language with the “Hello, World!” code!
/14-hello-match
directory.npm install
to install the necessary dependencies.npm run serve
.By default, the app should be accessible at http://localhost:8080.
For my second Vue project, I wanted to keep things fairly simple while learning about communication between parent and child components using emissions.
Interestingly, the bulk of the work was the collecting of language data:
Any language that had a devicon logo and a Hello World that was short enough to fit on the card was used.
Because several languages share the same Hello World program (3 of them are simply print("Hello, world!")
), a file name is also provided to be able to distinguish between code that could apply to multiple languages.
Our game board is represented by an Array
of card objects, which have the following properties:
language
, the language that the card represents,type
, the type of card it is: either logo
or code
,index
, the card’s index in the card array (used for easily flipping cards),isFlipped
, whether the card is currently flipped, andisMatched
, whether the card has been matched with its counterpart.The board array is populated using the newGame
method, which is called beforeMount
or whenever the user clicks the “New Game” button:
newGame() {
// The number of languages on the board, which is the number of cards / 2
const numLanguages = difficulties[this.difficulty]**2 / 2;
// Shuffle the languages and select the right amount
const boardLanguages = shuffle(languages).slice(0, numLanguages);
// Initialize a new empty board
const board = [];
// For each language, push a logo and code card to the new board
boardLanguages.forEach(language => {
const card1 = { language, type: 'logo', isFlipped: false, isMatched: false };
const card2 = { language, type: 'code', isFlipped: false, isMatched: false };
board.push(card1, card2);
});
// Save the new board in state
this.board = shuffle(board);
},
Cards are rendered based on their individual states. If a card isFlipped
or isMatched
, it’s face-up, otherwise it’s face-down. We also have a flip
method, which attempts to flip a card when it’s clicked:
flip(index) {
// Get any cards on the board that are already flipped
const flipped = this.board.filter(card => card.isFlipped);
// If two cards are flipped (i.e. they're about to be unflipped)
// then do nothing.
if (flipped.length > 1) return;
// If only one card is flipped
else if (flipped.length === 1) {
// Get the card that's already flipped and the card that
// is going to be flipped
const card1 = flipped[0];
const card2 = this.board[index];
// If they're a match (i.e. they have the same language value)
// Then set both isMatched attributes to true
if (card1.language === card2.language) {
card1.isMatched = true;
card2.isMatched = true;
card1.isFlipped = false;
// Check to see if the player has won
this.checkForWin();
// Otherwise, the two cards don't match
} else {
// Temporarily flip the card
card2.isFlipped = true;
// Unflip both cards in 1.5 seconds
setTimeout(() => {
card1.isFlipped = false;
card2.isFlipped = false;
}, 1500);
}
// Otherwise, this is the first card to be flipped,
} else {
// so flip it
this.board[index].isFlipped = true;
}
}
Other than our changeDifficulty
method and our checkForWin
method (which simply checks to see if the number of matched cards is equal to the total number of cards), this is our main functionality.
We do any syntax highlighting with Highlight.js on the game board’s updated
event using hljs.highlightAll()
. Any supported languages are tagged accordingly. Though highlightAll()
will be called quite a bit, it should only perform all of the highlighting on its first invokation.
We also use canvas-confetti to provide some fanfare when the game has been won.