Skip to content

Instantly share code, notes, and snippets.

@Kvnbbg
Created July 26, 2024 20:53
Show Gist options
  • Save Kvnbbg/214d766e30fb64c454764035901e4806 to your computer and use it in GitHub Desktop.
Save Kvnbbg/214d766e30fb64c454764035901e4806 to your computer and use it in GitHub Desktop.
Chess Player MVP

Chess Player MVP

Experience a dynamic chess game built with Vue.js that includes a multilingual interface, timer modes, and a task manager for enhanced productivity. This application supports English, French, Spanish, and German, providing a seamless gaming and task management experience.

A Pen by Kevin Marville on CodePen.

License.

<template>
<div id="app">
<header>
<nav>
<ul>
<li>
<a href="#">{{ translations[locale].home }}</a>
</li>
<li>
<a href="#">{{ translations[locale].about }}</a>
</li>
<li>
<a href="#">{{ translations[locale].contact }}</a>
</li>
</ul>
</nav>
<div class="auth-buttons">
<button @click="showLogin = true">
{{ translations[locale].login }}
</button>
<button @click="showRegister = true">
{{ translations[locale].register }}
</button>
</div>
</header>
<main>
<h1>{{ translations[locale].gameTitle }}</h1>
<div class="timer-settings">
<label>{{ translations[locale].timerMode }}</label>
<select v-model="timerMode">
<option value="none">{{ translations[locale].none }}</option>
<option value="blitz">{{ translations[locale].blitz }}</option>
<option value="bullet">{{ translations[locale].bullet }}</option>
</select>
<button @click="startGame">{{ translations[locale].startGame }}</button>
</div>
<div class="board">
<div
v-for="(square, index) in board"
:key="index"
class="square"
:class="getSquareColor(index)"
@click="handleSquareClick(index)"
>
<img v-if="square" :src="getPieceImage(square)" alt="Piece" />
</div>
</div>
<div class="timer-section" v-if="timerMode !== 'none'">
<h2>{{ formattedTime }}</h2>
<button @click="pauseResumeTimer">
{{
isRunning ? translations[locale].pause : translations[locale].resume
}}
</button>
</div>
<div class="tasks">
<h2>{{ translations[locale].tasks }}</h2>
<input
v-model="newTask"
type="text"
:placeholder="translations[locale].addNewTask"
@keyup.enter="addTask"
/>
<ul>
<li v-for="(task, index) in tasks" :key="index">
<input
type="checkbox"
v-model="task.completed"
@change="completeTask(index)"
/>
{{ task.name }}
<button @click="deleteTask(index)">&#x2716;</button>
</li>
</ul>
</div>
</main>
<footer>
<p>
Check out my
<a href="https://linkedin.com/in/kevin-marville" target="_blank">
LinkedIn
</a>
</p>
</footer>
<div class="floating-bubble">
<a href="https://kvnbbg-creations.io" target="_blank">
Je cherche de nouvelles opportunités sur le marché du travail
aujourd'hui
<span>🐼🌱</span>
</a>
</div>
<audio ref="audio" :src="alarmSound"></audio>
</div>
</template>
<script>
export default {
data() {
return {
locale: "en",
translations: {
en: {
home: "Home",
about: "About",
contact: "Contact",
login: "Login",
register: "Register",
gameTitle: "Vue.js Chess Game",
timerMode: "Timer Mode",
none: "None",
blitz: "Blitz",
bullet: "Bullet",
startGame: "Start Game",
start: "Start",
pause: "Pause",
resume: "Resume",
reset: "Reset",
tasks: "Tasks",
addNewTask: "Add a new task",
loginSuccess: "You have successfully logged in!",
loginFail: "Login failed. Please check your username and password.",
registerSuccess: "You have successfully registered!",
registerFail: "Registration failed. Please check your details."
},
fr: {
home: "Accueil",
about: "À propos",
contact: "Contact",
login: "Connexion",
register: "Inscription",
gameTitle: "Jeu d'échecs Vue.js",
timerMode: "Mode minuterie",
none: "Aucun",
blitz: "Blitz",
bullet: "Bullet",
startGame: "Commencer le jeu",
start: "Démarrer",
pause: "Pause",
resume: "Reprendre",
reset: "Réinitialiser",
tasks: "Tâches",
addNewTask: "Ajouter une nouvelle tâche",
loginSuccess: "Vous vous êtes connecté avec succès!",
loginFail:
"Échec de la connexion. Veuillez vérifier votre nom d'utilisateur et votre mot de passe.",
registerSuccess: "Vous vous êtes inscrit avec succès!",
registerFail: "Échec de l'inscription. Veuillez vérifier vos détails."
},
es: {
home: "Inicio",
about: "Acerca de",
contact: "Contacto",
login: "Iniciar sesión",
register: "Registrarse",
gameTitle: "Juego de ajedrez Vue.js",
timerMode: "Modo de temporizador",
none: "Ninguno",
blitz: "Blitz",
bullet: "Bullet",
startGame: "Iniciar juego",
start: "Comenzar",
pause: "Pausa",
resume: "Reanudar",
reset: "Restablecer",
tasks: "Tareas",
addNewTask: "Agregar una nueva tarea",
loginSuccess: "¡Has iniciado sesión con éxito!",
loginFail:
"Error de inicio de sesión. Por favor, verifique su nombre de usuario y contraseña.",
registerSuccess: "¡Te has registrado con éxito!",
registerFail: "Error de registro. Por favor, verifique sus datos."
},
de: {
home: "Startseite",
about: "Über",
contact: "Kontakt",
login: "Anmelden",
register: "Registrieren",
gameTitle: "Vue.js Schachspiel",
timerMode: "Timer-Modus",
none: "Keiner",
blitz: "Blitz",
bullet: "Bullet",
startGame: "Spiel starten",
start: "Start",
pause: "Pause",
resume: "Fortsetzen",
reset: "Zurücksetzen",
tasks: "Aufgaben",
addNewTask: "Neue Aufgabe hinzufügen",
loginSuccess: "Sie haben sich erfolgreich angemeldet!",
loginFail:
"Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort.",
registerSuccess: "Sie haben sich erfolgreich registriert!",
registerFail:
"Registrierung fehlgeschlagen. Bitte überprüfen Sie Ihre Angaben."
}
},
gameTitle: "Vue.js Chess Game",
board: this.initializeBoard(),
selectedSquare: null,
turn: "white",
pieceImages: {
wp: "path/to/white-pawn.png",
wr: "path/to/white-rook.png",
wn: "path/to/white-knight.png",
wb: "path/to/white-bishop.png",
wq: "path/to/white-queen.png",
wk: "path/to/white-king.png",
bp: "path/to/black-pawn.png",
br: "path/to/black-rook.png",
bn: "path/to/black-knight.png",
bb: "path/to/black-bishop.png",
bq: "path/to/black-queen.png",
bk: "path/to/black-king.png"
},
time: 0,
timer: null,
isRunning: false,
timerMode: "none",
tasks: [],
newTask: "",
alarmSound: "https://www.soundjay.com/button/beep-07.wav",
showLogin: false,
showRegister: false,
username: "",
password: "",
confirmPassword: ""
};
},
computed: {
formattedTime() {
const minutes = Math.floor(this.time / 60);
const seconds = this.time % 60;
return `${minutes
.toString()
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
}
},
methods: {
initializeBoard() {
return [
"br",
"bn",
"bb",
"bq",
"bk",
"bb",
"bn",
"br",
"bp",
"bp",
"bp",
"bp",
"bp",
"bp",
"bp",
"bp",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"wp",
"wp",
"wp",
"wp",
"wp",
"wp",
"wp",
"wp",
"wr",
"wn",
"wb",
"wq",
"wk",
"wb",
"wn",
"wr"
];
},
getSquareColor(index) {
const row = Math.floor(index / 8);
const col = index % 8;
return (row + col) % 2 === 0 ? "light-square" : "dark-square";
},
getPieceImage(piece) {
return this.pieceImages[piece];
},
handleSquareClick(index) {
if (this.selectedSquare === null) {
if (this.board[index] && this.board[index][0] === this.turn[0]) {
this.selectedSquare = index;
}
} else {
const validMove = this.isValidMove(this.selectedSquare, index);
if (validMove) {
this.board[index] = this.board[this.selectedSquare];
this.board[this.selectedSquare] = "";
this.selectedSquare = null;
this.turn = this.turn === "white" ? "black" : "white";
this.autoPlay();
} else {
this.selectedSquare = null;
}
}
},
isValidMove(fromIndex, toIndex) {
const piece = this.board[fromIndex];
const target = this.board[toIndex];
if (target && target[0] === piece[0]) {
return false;
}
if (piece[1] === "p") {
const direction = piece[0] === "w" ? -1 : 1;
const startRow = piece[0] === "w" ? 6 : 1;
const fromRow = Math.floor(fromIndex / 8);
const toRow = Math.floor(toIndex / 8);
const toCol = toIndex % 8;
const fromCol = fromIndex % 8;
if (fromCol === toCol && !target) {
if (fromRow + direction === toRow) {
return true;
}
if (fromRow === startRow && fromRow + 2 * direction === toRow) {
return true;
}
}
if (
Math.abs(fromCol - toCol) === 1 &&
fromRow + direction === toRow &&
target
) {
return true;
}
}
return false;
},
autoPlay() {
setTimeout(() => {
const possibleMoves = this.getAllPossibleMoves(this.turn);
if (possibleMoves.length > 0) {
const randomMove =
possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
this.board[randomMove.to] = this.board[randomMove.from];
this.board[randomMove.from] = "";
this.turn = this.turn === "white" ? "black" : "white";
this.autoPlay();
}
}, 1000);
},
getAllPossibleMoves(turn) {
const moves = [];
this.board.forEach((piece, index) => {
if (piece && piece[0] === turn[0]) {
for (let toIndex = 0; toIndex < 64; toIndex++) {
if (this.isValidMove(index, toIndex)) {
moves.push({ from: index, to: toIndex });
}
}
}
});
return moves;
},
startGame() {
if (this.timerMode === "blitz") {
this.time = 5 * 60;
} else if (this.timerMode === "bullet") {
this.time = 1 * 60;
}
this.isRunning = true;
this.startTimer();
},
startTimer() {
this.timer = setInterval(() => {
if (this.time > 0) {
this.time--;
} else {
clearInterval(this.timer);
this.isRunning = false;
}
}, 1000);
},
pauseResumeTimer() {
if (this.isRunning) {
clearInterval(this.timer);
} else {
this.startTimer();
}
this.isRunning = !this.isRunning;
},
resetTimer() {
clearInterval(this.timer);
this.time = 0;
this.isRunning = false;
},
addTask() {
if (this.newTask.trim()) {
this.tasks.push({ name: this.newTask, completed: false });
this.newTask = "";
}
},
deleteTask(index) {
this.tasks.splice(index, 1);
},
completeTask(index) {
if (this.tasks[index].completed) {
this.deleteTask(index);
}
},
login() {
if (this.username && this.password) {
alert(
`Bravo ${this.username}! ${
this.translations[this.locale].loginSuccess
}`
);
this.showLogin = false;
} else {
alert(this.translations[this.locale].loginFail);
}
},
register() {
if (
this.username &&
this.password &&
this.password === this.confirmPassword
) {
alert(
`Bravo ${this.username}! ${
this.translations[this.locale].registerSuccess
}`
);
this.showRegister = false;
} else {
alert(this.translations[this.locale].registerFail);
}
}
},
beforeDestroy() {
clearInterval(this.timer);
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
header nav ul {
list-style: none;
padding: 0;
}
header nav ul li {
display: inline;
margin: 0 10px;
}
.auth-buttons {
margin-top: 20px;
}
.board {
display: grid;
grid-template-columns: repeat(8, 50px);
grid-template-rows: repeat(8, 50px);
gap: 0;
margin: 20px auto;
border: 2px solid #000;
}
.square {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.light-square {
background-color: #f0d9b5;
}
.dark-square {
background-color: #b58863;
}
.square img {
width: 100%;
height: auto;
}
footer {
margin-top: 20px;
}
footer a {
color: #4fc08d;
text-decoration: none;
}
.floating-bubble {
position: fixed;
bottom: 20px;
right: 20px;
background: #4fc08d;
color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
animation: float 5s ease-in-out infinite;
}
.floating-bubble a {
color: #fff;
text-decoration: none;
}
@keyframes float {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
.timer-settings {
margin: 20px 0;
}
.timer-section {
margin: 20px 0;
}
.tasks {
text-align: left;
margin-top: 20px;
}
.tasks h2 {
font-size: 1.5em;
margin-bottom: 10px;
}
.tasks ul {
list-style: none;
padding: 0;
}
.tasks li {
background: #ececec;
border: 1px solid #ddd;
margin: 10px 0;
padding: 10px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tasks input[type="text"] {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
width: calc(100% - 40px);
}
.tasks input[type="checkbox"] {
margin-right: 10px;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment