Initial Commit
This commit is contained in:
commit
ddf09fe00c
113 changed files with 187148 additions and 0 deletions
52
app/Console/Commands/CardExport.php
Normal file
52
app/Console/Commands/CardExport.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use App\Models\Character;
|
||||
use App\Models\Raid;
|
||||
use App\Models\Card;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CardExport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'card:export';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Export card data';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$data = collect();
|
||||
foreach(Card::with(['character', 'raid'])->get() as $card) {
|
||||
|
||||
$row = collect([
|
||||
'message' => $card->body,
|
||||
'character' => $card->character ? $card->character->name : null,
|
||||
'raid' => $card->raid ? $card->raid->name : null,
|
||||
'class' => $card->class,
|
||||
'role' => $card->role
|
||||
])->filter()->toArray();
|
||||
|
||||
$data->push($row);
|
||||
}
|
||||
|
||||
$this->line($data->toJson(JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
74
app/Console/Commands/CardImport.php
Normal file
74
app/Console/Commands/CardImport.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use App\Models\Character;
|
||||
use App\Models\Raid;
|
||||
use App\Models\Card;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CardImport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'card:import {file}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import card data';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$file = $this->argument('file');
|
||||
|
||||
$data = file_get_contents($file);
|
||||
|
||||
try {
|
||||
$json = json_decode($data, false, 8, JSON_THROW_ON_ERROR);
|
||||
} catch(\JsonException $ex) {
|
||||
$this->error("Json: " . $ex->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($json as $item) {
|
||||
|
||||
$data = [
|
||||
'body' => $item->message,
|
||||
];
|
||||
|
||||
if (isset($item->character)) {
|
||||
$character = Character::firstOrCreate(['name' => $item->character]);
|
||||
$data['character_id'] = $character->id;
|
||||
}
|
||||
|
||||
if (isset($item->raid)) {
|
||||
$raid = Raid::firstOrCreate(['name' => $item->raid]);
|
||||
$data['raid_id'] = $raid->id;
|
||||
}
|
||||
|
||||
if (isset($item->class)) {
|
||||
$data['class'] = Str::lower($item->class);
|
||||
}
|
||||
|
||||
if (isset($item->role)) {
|
||||
$data['role'] = $item->role;
|
||||
}
|
||||
|
||||
Card::insert($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
app/Console/Kernel.php
Normal file
44
app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\CardImport::class,
|
||||
Commands\CardExport::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('model:prune', [ '--model' => [ \App\Models\Setting::class ] ])
|
||||
->description("Prune expired settings")
|
||||
->everyTwoHours();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
41
app/Exceptions/Handler.php
Normal file
41
app/Exceptions/Handler.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
10
app/Game/Exceptions/Exception.php
Normal file
10
app/Game/Exceptions/Exception.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game\Exceptions;
|
||||
|
||||
use Exception as BaseException;
|
||||
|
||||
class Exception extends BaseException
|
||||
{
|
||||
//
|
||||
}
|
||||
8
app/Game/Exceptions/NotEnoughCardsException.php
Normal file
8
app/Game/Exceptions/NotEnoughCardsException.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game\Exceptions;
|
||||
|
||||
class NotEnoughCardsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
93
app/Game/GameBoard.php
Normal file
93
app/Game/GameBoard.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game;
|
||||
|
||||
use App\Models\Card;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use App\Game\Exceptions\NotEnoughCardsException;
|
||||
|
||||
class GameBoard
|
||||
{
|
||||
/**
|
||||
* Collection of the cards on the board.
|
||||
*/
|
||||
protected Collection $cards;
|
||||
|
||||
/**
|
||||
* State of the cards on the board.
|
||||
*/
|
||||
protected GameBoardState $state;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new board.
|
||||
*/
|
||||
public function __construct(GameBoardState $state)
|
||||
{
|
||||
$this->state = $state;
|
||||
$this->regenerate(new GameSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cards.
|
||||
*/
|
||||
public function getCards()
|
||||
{
|
||||
return $this->cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state
|
||||
*/
|
||||
public function getState() : GameBoardState
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the board.
|
||||
*/
|
||||
public function getSize() : int
|
||||
{
|
||||
return $this->state->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the game board.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->state->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the board is in an ended state.
|
||||
* E.g: all cards is in a winning state.
|
||||
*/
|
||||
public function hasGameEnded() : bool
|
||||
{
|
||||
return $this->state->isFull(GameBoardState::STATE_WIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the board with new cards.
|
||||
*/
|
||||
public function regenerate(GameSettings $settings) : self
|
||||
{
|
||||
$num_cards = $this->getSize();
|
||||
$cards = Card::getBySettings($settings, $num_cards);
|
||||
|
||||
if (count($cards) < $num_cards) {
|
||||
$message = sprintf("Impossible to generate cards: requested %d but only %d returned", $num_cards, count($cards));
|
||||
throw new NotEnoughCardsException($message);
|
||||
}
|
||||
|
||||
$this->cards = $cards;
|
||||
|
||||
// Reset the state if we set new cards.
|
||||
$this->clear();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
241
app/Game/GameBoardState.php
Normal file
241
app/Game/GameBoardState.php
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game;
|
||||
|
||||
class GameBoardState
|
||||
{
|
||||
/**
|
||||
* Cell states. (unset is default state)
|
||||
*/
|
||||
const STATE_PRESSED = 0;
|
||||
const STATE_WIN = 1;
|
||||
|
||||
/**
|
||||
* Width of the board
|
||||
*/
|
||||
protected int $width;
|
||||
|
||||
/**
|
||||
* Height of the board
|
||||
*/
|
||||
protected int $height;
|
||||
|
||||
/**
|
||||
* State of the cells.
|
||||
*/
|
||||
protected array $state = [];
|
||||
|
||||
/**
|
||||
* If the board is in a winning state or not.
|
||||
*/
|
||||
protected int $winning = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($width, $height)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of a cell.
|
||||
*/
|
||||
public function set(int $pos, bool $pressed = true) : self
|
||||
{
|
||||
if ($this->hasGameEnded()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($pressed) {
|
||||
$this->state[$pos] = self::STATE_PRESSED;
|
||||
} else {
|
||||
unset($this->state[$pos]);
|
||||
}
|
||||
|
||||
$this->update();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle pressed state of a cell.
|
||||
*/
|
||||
public function toggle(int $pos) : self
|
||||
{
|
||||
return $this->set($pos, !$this->isPressed($pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cell is pressed or not.
|
||||
*/
|
||||
public function isPressed(int $pos) : bool
|
||||
{
|
||||
return isset($this->state[$pos]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cell is part of a winning row/column.
|
||||
*/
|
||||
public function isWin(int $pos) : bool
|
||||
{
|
||||
return isset($this->state[$pos]) && $this->state[$pos] == self::STATE_WIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sate of the cells.
|
||||
*/
|
||||
public function getState() : array
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the board.
|
||||
*/
|
||||
public function getSize() : int
|
||||
{
|
||||
return $this->width * $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the board.
|
||||
*/
|
||||
public function clear() : self
|
||||
{
|
||||
$this->state = [];
|
||||
$this->winning = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// TODO: Rename
|
||||
public function getNumWinRows() : int
|
||||
{
|
||||
return $this->winning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the board only has winning cards
|
||||
*/
|
||||
public function hasGameEnded() : bool
|
||||
{
|
||||
if (count($this->state) < $this->width * $this->height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->state as $pos) {
|
||||
if ($pos !== self::STATE_WIN) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the board is full and all positions is in a given state.
|
||||
*/
|
||||
public function isFull($state) : bool
|
||||
{
|
||||
// Board not filled, impossible to have all positions set.
|
||||
if (count($this->state) < $this->width * $this->height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->state as $pos) {
|
||||
if ($pos !== $state) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function update()
|
||||
{
|
||||
foreach($this->state as $pos => $_) {
|
||||
$this->state[$pos] = self::STATE_PRESSED;
|
||||
}
|
||||
|
||||
$patterns = $this->checkWinningState();
|
||||
foreach($patterns as $cards) {
|
||||
foreach($cards as $pos) {
|
||||
$this->state[$pos] = self::STATE_WIN;
|
||||
}
|
||||
}
|
||||
$this->winning = count($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the board is in a winning state.
|
||||
*/
|
||||
protected function checkWinningState() : array
|
||||
{
|
||||
$win = [];
|
||||
|
||||
// Rows
|
||||
for($y=0; $y < $this->height; $y++) {
|
||||
$c = $this->checkRow($y);
|
||||
if (count($c)) {
|
||||
$win[] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
for($x=0; $x < $this->width; $x++) {
|
||||
$c = $this->checkCol($x);
|
||||
if (count($c)) {
|
||||
$win[] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
return $win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a row is in a winning state (all cells pressed)
|
||||
*/
|
||||
protected function checkRow($y) : array
|
||||
{
|
||||
$cards = [];
|
||||
|
||||
$y = $y * $this->width;
|
||||
for($x = 0; $x < $this->width; $x++) {
|
||||
$pos = $x + $y;
|
||||
if (!$this->isPressed($pos)) {
|
||||
return [];
|
||||
}
|
||||
/*
|
||||
if ($this->state[$pos] == self::STATE_WIN) {
|
||||
continue;
|
||||
} */
|
||||
$cards[] = $pos;
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column is in a winning state (all cells pressed)
|
||||
*/
|
||||
protected function checkCol($x) : array
|
||||
{
|
||||
$cards = [];
|
||||
|
||||
for($y = 0; $y < $this->height; $y++) {
|
||||
$pos = $x + ($y * $this->width);
|
||||
if (!$this->isPressed($pos)) {
|
||||
return [];
|
||||
}
|
||||
/*
|
||||
if ($this->state[$pos] == self::STATE_WIN) {
|
||||
continue;
|
||||
} */
|
||||
$cards[] = $pos;
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
}
|
||||
36
app/Game/GameService.php
Normal file
36
app/Game/GameService.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game;
|
||||
|
||||
class GameService
|
||||
{
|
||||
protected GameSession $session;
|
||||
|
||||
public function __construct(GameSession $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function session() : GameSession
|
||||
{
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new Game session
|
||||
*/
|
||||
public function newSession(?GameSettings $settings = null) : self
|
||||
{
|
||||
// Generate new cards
|
||||
if (!$settings) {
|
||||
$settings = $this->session()->getSettings();
|
||||
}
|
||||
|
||||
$this->session->getBoard()->regenerate($settings);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
69
app/Game/GameSession.php
Normal file
69
app/Game/GameSession.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class GameSession
|
||||
{
|
||||
/**
|
||||
* The Game Board
|
||||
*/
|
||||
protected GameBoard $board;
|
||||
|
||||
/**
|
||||
* Game settings for this session.
|
||||
*/
|
||||
protected GameSettings $settings;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
//protected $cards;
|
||||
|
||||
public function __construct(GameBoard $board, GameSettings $settings)
|
||||
{
|
||||
$this->board = $board;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getBoard() : GameBoard
|
||||
{
|
||||
return $this->board;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getBoardState() : GameBoardState
|
||||
{
|
||||
return $this->getBoard()->getState();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getCards() : Collection
|
||||
{
|
||||
return $this->getBoard()->getCards();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getSettings() : GameSettings
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setSettings(GameSettings $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
}
|
||||
66
app/Game/GameSettings.php
Normal file
66
app/Game/GameSettings.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace App\Game;
|
||||
|
||||
use App\Support\Set;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class GameSettings
|
||||
{
|
||||
public Set $classes;
|
||||
|
||||
public Set $roles;
|
||||
|
||||
public Set $characters;
|
||||
|
||||
public Set $raids;
|
||||
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
$this->classes = new Set();
|
||||
$this->characters = new Set();
|
||||
$this->raids = new Set();
|
||||
$this->roles = new Set();
|
||||
$this->fromArray($settings);
|
||||
}
|
||||
|
||||
public function fromArray(array $array)
|
||||
{
|
||||
if (isset($array['classes'])) {
|
||||
$this->classes->fill($array['classes']);
|
||||
}
|
||||
|
||||
if (isset($array['raids'])) {
|
||||
$this->raids->fill($array['raids']);
|
||||
}
|
||||
|
||||
if (isset($array['characters'])) {
|
||||
$this->characters->fill($array['characters']);
|
||||
}
|
||||
|
||||
if (isset($array['roles'])) {
|
||||
$this->roles->fill($array['roles']);
|
||||
}
|
||||
}
|
||||
|
||||
public function toArray() : array
|
||||
{
|
||||
return [
|
||||
'classes' => $this->classes->toArray(),
|
||||
'roles' => $this->roles->toArray(),
|
||||
'raids' => $this->raids->toArray(),
|
||||
'characters' => $this->characters->toArray()
|
||||
];
|
||||
}
|
||||
|
||||
public function all() : Collection
|
||||
{
|
||||
return collect([
|
||||
'classes' => $this->classes->all()->toArray(),
|
||||
'roles' => $this->roles->all()->toArray(),
|
||||
'raids' => $this->raids->all()->toArray(),
|
||||
'characters' => $this->characters->all()->toArray()
|
||||
])->filter();
|
||||
}
|
||||
}
|
||||
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
||||
17
app/Http/Controllers/SettingController.php
Normal file
17
app/Http/Controllers/SettingController.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Game\GameService;
|
||||
use App\Models\Setting;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
public function hash(GameService $service, Setting $setting)
|
||||
{
|
||||
// Generate a new game with these settings.
|
||||
$service->newSession($setting->value);
|
||||
|
||||
return redirect()->route('game');
|
||||
}
|
||||
}
|
||||
36
app/Http/Kernel.php
Normal file
36
app/Http/Kernel.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
]
|
||||
];
|
||||
}
|
||||
62
app/Http/Livewire/Game.php
Normal file
62
app/Http/Livewire/Game.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
class Game extends GameComponent
|
||||
{
|
||||
/**
|
||||
* Get the game session
|
||||
*/
|
||||
public function getSessionProperty()
|
||||
{
|
||||
return $this->service->session();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the game board.
|
||||
*/
|
||||
public function getBoardProperty()
|
||||
{
|
||||
return $this->session->getBoard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the game state.
|
||||
*/
|
||||
public function getStateProperty()
|
||||
{
|
||||
return $this->session->getBoardState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear everything.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->board->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered wen a card is pressed.
|
||||
*/
|
||||
public function toggle($pos)
|
||||
{
|
||||
$this->state->toggle($pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset game session.
|
||||
*/
|
||||
public function restart()
|
||||
{
|
||||
$this->service->newSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the game board.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('game');
|
||||
}
|
||||
}
|
||||
18
app/Http/Livewire/GameComponent.php
Normal file
18
app/Http/Livewire/GameComponent.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Game\GameService;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
abstract class GameComponent extends Component
|
||||
{
|
||||
/**
|
||||
* Get the game service object
|
||||
*/
|
||||
public function getServiceProperty() : GameService
|
||||
{
|
||||
return app()->make(GameService::class);
|
||||
}
|
||||
}
|
||||
78
app/Http/Livewire/Setup.php
Normal file
78
app/Http/Livewire/Setup.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\Raid;
|
||||
use App\Models\Character;
|
||||
use App\Models\Card;
|
||||
|
||||
class Setup extends GameComponent
|
||||
{
|
||||
public $all_raids;
|
||||
|
||||
public $all_characters;
|
||||
|
||||
public $all_classes;
|
||||
|
||||
public $shared_settings;
|
||||
|
||||
public $values = [
|
||||
'classes' => [],
|
||||
'raids' => [],
|
||||
'characters' => []
|
||||
];
|
||||
|
||||
function mount()
|
||||
{
|
||||
$this->all_classes = Card::select('class')
|
||||
->whereNotNull('class')
|
||||
->groupBy('class')->orderBy('class')->get()->pluck('class');
|
||||
|
||||
$this->all_raids = Raid::select('id', 'name')->orderBy('name')->get();
|
||||
$this->all_characters = Character::select('id', 'name')
|
||||
->orderBy('name')->get();
|
||||
|
||||
// Load values from settings
|
||||
$this->values['classes'] = $this->settings->classes->toArray();
|
||||
$this->values['raids'] = $this->settings->raids->toArray();
|
||||
$this->values['characters'] = $this->settings->characters->toArray();
|
||||
}
|
||||
|
||||
public function getSettingsProperty()
|
||||
{
|
||||
return $this->service->session()->getSettings();
|
||||
}
|
||||
|
||||
public function resetSettings()
|
||||
{
|
||||
$this->reset('values');
|
||||
}
|
||||
|
||||
public function share()
|
||||
{
|
||||
$setting = Setting::create(['value' => $this->values]);
|
||||
|
||||
$this->shared_settings = $setting->hash;
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
// Store values
|
||||
$this->settings->classes->fill($this->values['classes']);
|
||||
$this->settings->raids->fill($this->values['raids']);
|
||||
$this->settings->characters->fill($this->values['characters']);
|
||||
|
||||
$this->service->newSession($this->settings);
|
||||
|
||||
$this->redirectRoute('game');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the setup page
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('setup');
|
||||
}
|
||||
}
|
||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
93
app/Models/Card.php
Normal file
93
app/Models/Card.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Game\GameSettings;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Card extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $appends = ['subject'];
|
||||
|
||||
public $fillable = [
|
||||
'body',
|
||||
'character_id',
|
||||
'raid_id',
|
||||
'class',
|
||||
'role'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the character that's associated with this card.
|
||||
*/
|
||||
public function character()
|
||||
{
|
||||
return $this->belongsTo(Character::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raid that's associated with this card.
|
||||
*/
|
||||
public function raid()
|
||||
{
|
||||
return $this->belongsTo(Raid::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Who, Where or What this card belongs to.
|
||||
*/
|
||||
public function getSubjectAttribute()
|
||||
{
|
||||
if ($this->character) {
|
||||
return $this->character->name;
|
||||
} else if ($this->class) {
|
||||
return Str::ucfirst($this->class);
|
||||
} else if ($this->role) {
|
||||
return Str::ucfirst($this->role);
|
||||
}
|
||||
return "Somebody";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cards depending on settings.
|
||||
*/
|
||||
public static function getBySettings(GameSettings $settings, ?int $max = null)
|
||||
{
|
||||
// Map type of setting to database column.
|
||||
$type_map = [
|
||||
'characters' => 'character_id',
|
||||
'raids' => 'raid_id',
|
||||
'classes' => 'class',
|
||||
'roles' => 'role'
|
||||
];
|
||||
|
||||
$query = self::query()
|
||||
->inRandomOrder();
|
||||
|
||||
// Run through all settings and apply filter.
|
||||
foreach($settings->all() as $type => $values) {
|
||||
|
||||
$column = $type_map[$type];
|
||||
|
||||
$query->where(function ($q) use ($column, $values) {
|
||||
$q->whereIn($column, $values)->orWhereNull($column);
|
||||
});
|
||||
}
|
||||
|
||||
if ($max !== null) {
|
||||
$query->limit($max);
|
||||
}
|
||||
|
||||
return $query->get()
|
||||
->makeHidden(['character', 'class', 'deleted_at']);
|
||||
}
|
||||
}
|
||||
45
app/Models/Casts/GameSettingsCaster.php
Normal file
45
app/Models/Casts/GameSettingsCaster.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Casts;
|
||||
|
||||
use App\Game\GameSettings;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class GameSettingsCaster implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return GameSettings
|
||||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
$value = json_decode($value, true);
|
||||
return new GameSettings($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param GameSettings|array $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$value = new GameSettings($value);
|
||||
} else if (!($value instanceof GameSettings)) {
|
||||
throw new InvalidArgumentException('The given value must be an GameSettings instance or array.');
|
||||
}
|
||||
return json_encode($value->toArray());
|
||||
}
|
||||
}
|
||||
23
app/Models/Character.php
Normal file
23
app/Models/Character.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Character extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public $fillable = [
|
||||
'name',
|
||||
];
|
||||
|
||||
public function cards()
|
||||
{
|
||||
return $this->hasMany(Card::class);
|
||||
}
|
||||
}
|
||||
23
app/Models/Raid.php
Normal file
23
app/Models/Raid.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Raid extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public $fillable = [
|
||||
'name',
|
||||
];
|
||||
|
||||
public function cards()
|
||||
{
|
||||
return $this->hasMany(Card::class);
|
||||
}
|
||||
}
|
||||
60
app/Models/Setting.php
Normal file
60
app/Models/Setting.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Vinkla\Hashids\Facades\Hashids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
class Setting extends Model
|
||||
{
|
||||
use HasFactory, Prunable;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public $fillable = [
|
||||
'value',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'value' => Casts\GameSettingsCaster::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function prunable()
|
||||
{
|
||||
return static::where('created_at', '<', now()->subDay(1));
|
||||
}
|
||||
|
||||
public function getHashAttribute()
|
||||
{
|
||||
return Hashids::encode($this->id);
|
||||
}
|
||||
|
||||
public function scopeHash($q, $hash)
|
||||
{
|
||||
return $q->where('id', Hashids::decode($hash));
|
||||
}
|
||||
|
||||
/*
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
if ($method == 'find' && is_string($parameters[0])) {
|
||||
$parameters[0] = Hashids::decode($$parameters[0]);
|
||||
}
|
||||
|
||||
return parent::__call($method, $parameters);
|
||||
} */
|
||||
}
|
||||
28
app/Providers/AppServiceProvider.php
Normal file
28
app/Providers/AppServiceProvider.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
55
app/Providers/GameServiceProvider.php
Normal file
55
app/Providers/GameServiceProvider.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Game\GameBuilder;
|
||||
use App\Game\GameBoardState;
|
||||
use App\Game\GameService;
|
||||
use App\Game\GameSession;
|
||||
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Contracts\Support\DeferrableProvider;
|
||||
|
||||
class GameServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(GameBoardState::class, function($app) {
|
||||
$width = (int) config('app.game.board.width', 4);
|
||||
$height = (int) config('app.game.board.height', 4);
|
||||
return new GameBoardState($width, $height);
|
||||
});
|
||||
|
||||
$this->app->singleton(GameService::class, function ($app) {
|
||||
// Try loading from session.
|
||||
$session_key = config('app.game.session_name', 'game.service');
|
||||
$service = Session::get($session_key);
|
||||
|
||||
// No service in session, create a new one and store it.
|
||||
if (!$service) {
|
||||
$game_session = $app->make(GameSession::class);
|
||||
$service = new GameService($game_session);
|
||||
Session::put($session_key, $service);
|
||||
}
|
||||
return $service;
|
||||
});
|
||||
|
||||
$this->app->alias(GameService::class, 'game');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [ 'game', GameService::class, GameBoardState::class ];
|
||||
}
|
||||
}
|
||||
31
app/Providers/RouteServiceProvider.php
Normal file
31
app/Providers/RouteServiceProvider.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Setting;
|
||||
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->routes(function () {
|
||||
Route::middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
|
||||
Route::bind('setting', function ($value) {
|
||||
return Setting::hash($value)->first();
|
||||
});
|
||||
}
|
||||
}
|
||||
74
app/Support/Set.php
Normal file
74
app/Support/Set.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Set implements \Countable
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
public function __construct($items = [])
|
||||
{
|
||||
foreach($items as $k => $v) {
|
||||
$this->set($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
public function set($key, bool $value = true) : self
|
||||
{
|
||||
if ($value) {
|
||||
$this->items[$key] = true;
|
||||
} else {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fill($array) : self
|
||||
{
|
||||
$this->clear();
|
||||
|
||||
foreach($array as $k => $v) {
|
||||
$this->set($k, $v);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has($key) : bool
|
||||
{
|
||||
return isset($this->items[$key]);
|
||||
}
|
||||
|
||||
public function toggle($key) : self
|
||||
{
|
||||
$this->set($key, !$this->has($key));
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function clear() : self
|
||||
{
|
||||
$this->items = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function all() : Collection
|
||||
{
|
||||
return collect($this->items)->keys();
|
||||
}
|
||||
|
||||
public function count() : int
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
54
app/View/Components/Card.php
Normal file
54
app/View/Components/Card.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
use App\Models\Card as CardModel;
|
||||
|
||||
class Card extends Component
|
||||
{
|
||||
/**
|
||||
* Card number
|
||||
*/
|
||||
public int $number;
|
||||
|
||||
/**
|
||||
* The card
|
||||
*/
|
||||
public CardModel $card;
|
||||
|
||||
/**
|
||||
* If the card is flipped
|
||||
*/
|
||||
public bool $flipped;
|
||||
|
||||
|
||||
/**
|
||||
* If the card is flagged as winning.
|
||||
*/
|
||||
public bool $win;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(int $number, CardModel $card, bool $flipped = false, bool $win = false)
|
||||
{
|
||||
$this->number = $number;
|
||||
$this->card = $card;
|
||||
$this->flipped = $flipped;
|
||||
$this->win = $win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('components.card');
|
||||
}
|
||||
}
|
||||
40
app/View/Components/CardText.php
Normal file
40
app/View/Components/CardText.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
use App\Models\Card;
|
||||
|
||||
class CardText extends Component
|
||||
{
|
||||
protected Card $card;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Card $card)
|
||||
{
|
||||
$this->card = $card;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$subject = preg_quote(__($this->card->subject));
|
||||
|
||||
// Replace non escaped '?' with subject and also Unescape escaped '?'
|
||||
$text = preg_replace(
|
||||
['/(?<!\\\\)\?/u', '/\\\\\\?/u'] ,
|
||||
["<strong>$subject</strong>", '?'],
|
||||
$this->card->body);
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue