Version 1.0 #102

Merged
FernandoJVideira merged 163 commits from develop into master 2026-01-03 14:49:00 +00:00
6 changed files with 307 additions and 6 deletions
Showing only changes of commit 117f007b83 - Show all commits
+7 -4
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Game; use App\Models\Game;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests\StoreGameRequest;
class GameController extends Controller class GameController extends Controller
{ {
@@ -56,9 +57,10 @@ class GameController extends Controller
/** /**
* Store a newly created resource in storage. * 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) public function show(Game $game)
{ {
// return $game;
} }
/** /**
@@ -74,7 +76,8 @@ class GameController extends Controller
*/ */
public function update(Request $request, Game $game) public function update(Request $request, Game $game)
{ {
// $game->update($request->validated());
return response()->json($game);
} }
/** /**
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreGameRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}
+15
View File
@@ -3,9 +3,24 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Http\Requests\StoreGameRequest;
class Game extends Model 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() public function winner()
{ {
return $this->belongsTo(User::class, "winner_user_id"); return $this->belongsTo(User::class, "winner_user_id");
+1 -1
View File
@@ -6,7 +6,7 @@
"": { "": {
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.0.0", "@tailwindcss/vite": "^4.0.0",
"axios": "^1.11.0", "axios": "^1.13.2",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"laravel-vite-plugin": "^2.0.0", "laravel-vite-plugin": "^2.0.0",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
+1 -1
View File
@@ -8,7 +8,7 @@
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.0.0", "@tailwindcss/vite": "^4.0.0",
"axios": "^1.11.0", "axios": "^1.13.2",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"laravel-vite-plugin": "^2.0.0", "laravel-vite-plugin": "^2.0.0",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
+255
View File
@@ -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,
}
})