From d7a6862dcb8fb404050bfb4146ca340800a833e9 Mon Sep 17 00:00:00 2001 From: Tiago Ramos <2222055@my.ipleiria.pt> Date: Thu, 11 Dec 2025 20:42:30 +0000 Subject: [PATCH] Added necessary stuff for authentication --- api/app/Http/Controllers/GameController.php | 11 +- api/app/Http/Requests/StoreGameRequest.php | 28 +++ api/app/Models/Game.php | 15 ++ api/package-lock.json | 2 +- api/package.json | 2 +- frontend/src/stores/game.js | 255 ++++++++++++++++++++ 6 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 api/app/Http/Requests/StoreGameRequest.php create mode 100644 frontend/src/stores/game.js diff --git a/api/app/Http/Controllers/GameController.php b/api/app/Http/Controllers/GameController.php index 1a38512..9f1f95c 100644 --- a/api/app/Http/Controllers/GameController.php +++ b/api/app/Http/Controllers/GameController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; use App\Models\Game; use Illuminate\Http\Request; +use App\Http\Requests\StoreGameRequest; class GameController extends Controller { @@ -56,9 +57,10 @@ class GameController extends Controller /** * Store a newly created resource in storage. */ - public function store(Request $request) + public function store(StoreGameRequest $request) { - // + $game = Game::create($request->validated()); + return response()->json($game, 201); } /** @@ -66,7 +68,7 @@ class GameController extends Controller */ public function show(Game $game) { - // + return $game; } /** @@ -74,7 +76,8 @@ class GameController extends Controller */ public function update(Request $request, Game $game) { - // + $game->update($request->validated()); + return response()->json($game); } /** diff --git a/api/app/Http/Requests/StoreGameRequest.php b/api/app/Http/Requests/StoreGameRequest.php new file mode 100644 index 0000000..5874a1f --- /dev/null +++ b/api/app/Http/Requests/StoreGameRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/api/app/Models/Game.php b/api/app/Models/Game.php index 44fe639..0336e66 100644 --- a/api/app/Models/Game.php +++ b/api/app/Models/Game.php @@ -3,9 +3,24 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use App\Http\Requests\StoreGameRequest; + class Game extends Model { + protected $fillable = [ + 'player1_id', + 'player2_id', + 'winner_id', + 'type', + 'status', + 'began_at', + 'ended_at', + 'total_time', + 'player1_moves', + 'player2_moves', + ]; + public function winner() { return $this->belongsTo(User::class, "winner_user_id"); diff --git a/api/package-lock.json b/api/package-lock.json index 58c569a..15f979c 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "@tailwindcss/vite": "^4.0.0", - "axios": "^1.11.0", + "axios": "^1.13.2", "concurrently": "^9.0.1", "laravel-vite-plugin": "^2.0.0", "tailwindcss": "^4.0.0", diff --git a/api/package.json b/api/package.json index 7686b29..6ab44ff 100644 --- a/api/package.json +++ b/api/package.json @@ -8,7 +8,7 @@ }, "devDependencies": { "@tailwindcss/vite": "^4.0.0", - "axios": "^1.11.0", + "axios": "^1.13.2", "concurrently": "^9.0.1", "laravel-vite-plugin": "^2.0.0", "tailwindcss": "^4.0.0", diff --git a/frontend/src/stores/game.js b/frontend/src/stores/game.js new file mode 100644 index 0000000..ae36656 --- /dev/null +++ b/frontend/src/stores/game.js @@ -0,0 +1,255 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { toast } from 'vue-sonner' +import { useApiStore } from './api' + +export const useGameStore = defineStore('game', () => { + const apiStore = useApiStore() + + // Game state + const game = ref({ + players: { + human: { hand: [], score: 0, wonCards: [] }, + bot: { hand: [], score: 0, wonCards: [] }, + }, + deck: [], + trump: null, + currentTrick: [], + lastWinner: null, + gameOver: false, + winner: null, + }) + + const gameStarted = ref(false) + const isHumanTurn = ref(true) + const selectedCard = ref(null) + const beganAt = ref(null) + const endedAt = ref(null) + const moves = ref([]) + + // Computed + const humanScore = computed(() => { + return game.value.players.human.wonCards.reduce((sum, card) => sum + card.value, 0) + }) + + const botScore = computed(() => { + return game.value.players.bot.wonCards.reduce((sum, card) => sum + card.value, 0) + }) + + // Initialize deck + const initializeDeck = () => { + const suits = ['♥', '♦', '♣', '♠'] + const ranks = ['2', '3', '4', '5', '6', '7', 'J', 'Q', 'K', 'A'] + const values = { '2': 0, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0, 'J': 2, 'Q': 3, 'K': 4, 'A': 11 } + + const deck = [] + for (const suit of suits) { + for (const rank of ranks) { + deck.push({ rank, suit, value: values[rank] }) + } + } + + return deck.sort(() => Math.random() - 0.5) + } + + // Start new game + const startGame = () => { + game.value.deck = initializeDeck() + game.value.trump = game.value.deck[game.value.deck.length - 1] + game.value.players.human.hand = game.value.deck.splice(0, 3) + game.value.players.bot.hand = game.value.deck.splice(0, 3) + game.value.currentTrick = [] + game.value.gameOver = false + game.value.winner = null + game.value.players.human.score = 0 + game.value.players.bot.score = 0 + game.value.players.human.wonCards = [] + game.value.players.bot.wonCards = [] + gameStarted.value = true + isHumanTurn.value = true + beganAt.value = new Date() + endedAt.value = null + moves.value = [] + } + + // Play card + const playCard = async (cardIndex) => { + if (!isHumanTurn.value || game.value.gameOver) return + + const card = game.value.players.human.hand[cardIndex] + game.value.currentTrick.push({ player: 'human', card }) + game.value.players.human.hand.splice(cardIndex, 1) + selectedCard.value = null + + // Track move + moves.value.push({ + player: 'human', + card, + timestamp: new Date(), + }) + + isHumanTurn.value = false + + // Bot plays + setTimeout(() => { + botPlay() + }, 1000) + } + + // Bot play logic + const botPlay = () => { + const botHand = game.value.players.bot.hand + let cardToPlay + + if (game.value.currentTrick.length === 0) { + cardToPlay = botHand[0] + } else { + const leadCard = game.value.currentTrick[0].card + const canWin = botHand.some((c) => canBeat(c, leadCard)) + + if (canWin) { + cardToPlay = botHand.filter((c) => canBeat(c, leadCard)).sort((a, b) => a.value - b.value)[0] + } else { + cardToPlay = botHand.sort((a, b) => a.value - b.value)[0] + } + } + + game.value.currentTrick.push({ player: 'bot', card: cardToPlay }) + game.value.players.bot.hand.splice(game.value.players.bot.hand.indexOf(cardToPlay), 1) + + // Track move + moves.value.push({ + player: 'bot', + card: cardToPlay, + timestamp: new Date(), + }) + + // Resolve trick + setTimeout(() => { + resolveTrick() + }, 500) + } + + // Check if card can beat + const canBeat = (card, leadCard) => { + if (card.suit === leadCard.suit) { + return rankValue(card.rank) > rankValue(leadCard.rank) + } + return card.suit === game.value.trump.suit + } + + // Get rank value for comparison + const rankValue = (rank) => { + const values = { '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, 'J': 11, 'Q': 12, 'K': 13, 'A': 14 } + return values[rank] || 0 + } + + // Resolve trick + const resolveTrick = () => { + const trick = game.value.currentTrick + const card1 = trick[0].card + const card2 = trick[1].card + + let winner + if (card2.suit === card1.suit) { + winner = rankValue(card2.rank) > rankValue(card1.rank) ? 'bot' : 'human' + } else if (card2.suit === game.value.trump.suit) { + winner = 'bot' + } else { + winner = 'human' + } + + game.value.lastWinner = winner + game.value.players[winner].wonCards.push(card1, card2) + + // Refill hands + refillHands(winner) + + game.value.currentTrick = [] + + // Check if game is over + if (game.value.players.human.hand.length === 0 && game.value.deck.length === 0) { + endGame() + } else { + isHumanTurn.value = winner === 'human' + if (winner === 'bot') { + setTimeout(() => botPlay(), 1000) + } + } + } + + // Refill hands + const refillHands = (winner) => { + const order = winner === 'human' ? ['human', 'bot'] : ['bot', 'human'] + + for (const player of order) { + while (game.value.players[player].hand.length < 3 && game.value.deck.length > 0) { + game.value.players[player].hand.push(game.value.deck.pop()) + } + } + } + + // End game + const endGame = () => { + game.value.gameOver = true + game.value.winner = humanScore.value > botScore.value ? 'human' : 'bot' + endedAt.value = new Date() + } + + // Save game to API + const saveGame = async () => { + const gameData = { + type: 'S', // Standalone game + status: 'E', // Ended + player1_moves: moves.value, + began_at: beganAt.value, + ended_at: endedAt.value, + total_time: Math.ceil((endedAt.value - beganAt.value) / 1000), + } + + return toast.promise(apiStore.postGame(gameData), { + loading: 'Sending data to API...', + success: () => { + return '[API] Game saved successfully' + }, + error: (data) => `[API] Error saving game - ${data?.response?.data?.message}`, + }) + } + + // Reset game + const reset = () => { + gameStarted.value = false + selectedCard.value = null + beganAt.value = null + endedAt.value = null + moves.value = [] + game.value = { + players: { + human: { hand: [], score: 0, wonCards: [] }, + bot: { hand: [], score: 0, wonCards: [] }, + }, + deck: [], + trump: null, + currentTrick: [], + lastWinner: null, + gameOver: false, + winner: null, + } + } + + return { + game, + gameStarted, + isHumanTurn, + selectedCard, + beganAt, + endedAt, + moves, + humanScore, + botScore, + startGame, + playCard, + saveGame, + reset, + } +}) -- 2.54.0