Skip to content

Commit

Permalink
Merge pull request #29 from vituchon/several-medium-refacts
Browse files Browse the repository at this point in the history
Several medium refacts
  • Loading branch information
vituchon authored Aug 21, 2024
2 parents 85a178e + 3ef8521 commit 2ed1645
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 70 deletions.
2 changes: 1 addition & 1 deletion model/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

type Match struct {
Players []Player `json:"players"`
ActionsByPlayer ActionsByPlayer `json:"actionsByPlayerUniqueKey"`
ActionsByPlayer ActionsByPlayer `json:"actionsByPlayer"`
ActionsLog PlayerActions `json:"playerActions"`
Cards MatchCards `json:"matchCards"`
FirstPlayerIndex int `json:"firstPlayerIndex"`
Expand Down
14 changes: 7 additions & 7 deletions presentation/web/assets/html/game.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,18 @@ <h2>Juego <i>{{ctr.game.name}}</i></h2>
<div class="section player-list-section">
<h3>Jugadores</h3>
<!-- the check performed at Games.addPlayer at joining a game ensures that won't be two players with the same name, but they could share it and they will actually share the player -->
<div class="game-player-header" ng-repeat="player in ctr.game.players track by player.name">
<span>{{$index+1}}. <span ng-class="{'player-name': true, 'current-turn-player' : ctr.currentTurnPlayer.name === player.name}">{{player.name}}</span></span> <!-- TODO: now game players have id, USE ID to avoid a clash! -->
<div class="game-player-header" ng-repeat="player in ctr.game.players track by player.id">
<span>{{$index+1}}. <span ng-class="{'player-name': true, 'current-turn-player' : (ctr.currentTurnPlayer.id === player.id)}">{{player.name}}</span></span>
<span ng-if="ctr.isPlayerGameOwner(ctr.game,player)" style="font-size: 2rem;">🪑</span>
<i ng-if="ctr.currentTurnPlayer.name === player.name && !ctr.isPlayerTurn">⏱️: {{ctr.remainingSecondsToPerformAction}}</i>
<i ng-if="(ctr.currentTurnPlayer.id === player.id) && !ctr.isPlayerTurn">⏱️: {{ctr.remainingSecondsToPerformAction}}</i>
<span style="width: 10px;"></span>
<!-- using ng-if=ng-show="ctr.player.name !== player.name" doesn't works here! -->
<div ng-show="ctr.player.name != player.name" class="materialButton raised colorButton see-player-button" title="chusmear al jugador" ng-click="peekPlayer = !peekPlayer">
<img ng-show="!peekPlayer" src="/presentation/web/assets/images/eyes_open.png" height="20px">
<img ng-show="peekPlayer" src="/presentation/web/assets/images/eyes_closed.png" height="20px">
</div>
<div ng-if="ctr.isMatchInProgress && peekPlayer" class="player-action">
<div ng-repeat="action in ctr.game.currentMatch.actionsByPlayerUniqueKey[ctr.playerToUniqueKey(player)]">
<div ng-repeat="action in ctr.game.currentMatch.actionsByPlayer.get(player)">
<player-action ng-if="$last" order="$index" ng-model="action"></player-action>
</div>
</div>
Expand Down Expand Up @@ -104,7 +104,7 @@ <h3>Jugadores</h3>
<div class="title">En mano</div>
<countdown-clock handler="ctr.countdownHandler" total-seconds="ctr.secondsToPerformAction" refresh-rate-in-milis="500" on-end="ctr.onEndCountdown()"></countdown-clock>
<div class="cards">
<div class="card" ng-repeat="card in ctr.game.currentMatch.matchCards.byPlayer[ctr.playerToUniqueKey(ctr.player)].hand">
<div class="card" ng-repeat="card in ctr.game.currentMatch.matchCards.byPlayer.get(ctr.player).hand">
<input ng-if="ctr.isPlayerTurn" type="radio" ng-model="ctr.selectedHandCard" ng-value="card">
<card ng-model="card" ng-click="ctr.isPlayerTurn && (ctr.selectedHandCard = card)"></card>
</div>
Expand Down Expand Up @@ -142,7 +142,7 @@ <h3 style="display: flex;align-items: center;">Acciones sugeridas por el&nbsp;<s
<div class="taken-cards-container" ng-show="ctr.isMatchInProgress" ng-if="showTakenCards">
<h3>Cartas tomadas</h3>
<div class="taken-cards-section">
<div class="card" ng-repeat="card in ctr.game.currentMatch.matchCards.byPlayer[ctr.playerToUniqueKey(ctr.player)].taken">
<div class="card" ng-repeat="card in ctr.game.currentMatch.matchCards.byPlayer.get(ctr.player).taken">
<card ng-model="card"></card>
</div>
</div>
Expand All @@ -152,7 +152,7 @@ <h3>Cartas tomadas</h3>
<div class="player-actions-container" ng-show="ctr.isMatchInProgress" ng-if="showPlayerActions">
<h3>Acciones</h3>
<div class="player-actions-section">
<div class="player-action" ng-repeat="playerAction in ctr.game.currentMatch.actionsByPlayerUniqueKey[ctr.playerToUniqueKey(ctr.player)]">
<div class="player-action" ng-repeat="playerAction in ctr.game.currentMatch.actionsByPlayer.get(ctr.player)">
<player-action order="$index" ng-model="playerAction"></player-action>
</div>
</div>
Expand Down
7 changes: 2 additions & 5 deletions presentation/web/assets/html/lobby.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<div class="panel lobby">

<h2>Lobby</h2>

<!-- <loading message="Cargando..." ng-if="ctr.loading"></loading> -->

<div id="player-registration-section">
Expand Down Expand Up @@ -32,8 +31,6 @@ <h3>Hola&nbsp;<strong>{{ ctr.player.name }}</strong>&nbsp;bienvenido!</h3>
</div>
</div>



<div id="games-and-cards-outer-container" class="section" ng-if="ctr.isPlayerRegistered">
<div id="games-and-cards-inner-container">
<div id="game-list-section" class="section" >
Expand Down Expand Up @@ -61,7 +58,7 @@ <h2>Juegos</h2>
<div style="display:flex; flex-direction: row; align-items: center; margin-top: 10px;">
<span>{{$index+1}}. {{game.name}}</span>
<span class="flex-space-filler"></span>
<button class="materialButton colorButton" ng-if="ctr.doesGameAcceptPlayers(game)" ng-disabled="ctr.loading" ng-click="ctr.joinGame(game, ctr.player)">Unirse</button>
<button class="materialButton colorButton" ng-if="ctr.doesGameAcceptPlayers(game)" ng-disabled="ctr.loading" ng-click="ctr.joinGame(game)">Unirse</button>
<button class="materialButton colorButton" ng-if="ctr.canDeleteGame(game, ctr.player)" style="background-color: firebrick;" ng-disabled="ctr.loading" ng-click="ctr.deleteGame(game, ctr.player)">Eliminar</button>
<button class="materialButton colorButton" ng-if="!ctr.doesGameAcceptPlayers(game)" style="background-color: yellow; color: black;" disabled >Partida en progreso</button>
</div>
Expand All @@ -72,7 +69,7 @@ <h2>Juegos</h2>
<option value="">Seleccionar juego</option>
</select>
<span class="flex-space-filler"></span>
<button class="materialButton colorButton" ng-if="ctr.selectedGame" ng-disabled="ctr.loading" ng-click="ctr.joinGame(ctr.selectedGame, ctr.player)">Unirse</button>
<button class="materialButton colorButton" ng-if="ctr.selectedGame" ng-disabled="ctr.loading" ng-click="ctr.joinGame(ctr.selectedGame)">Unirse</button>
<button class="materialButton colorButton" style="background-color: red; color: yellow;" ng-if="ctr.selectedGame && ctr.canDeleteGame(ctr.selectedGame, ctr.player)" ng-disabled="ctr.loading" ng-click="ctr.deleteGame(ctr.selectedGame, ctr.player)">Eliminar</button>
</div>
</div>
Expand Down
13 changes: 2 additions & 11 deletions presentation/web/assets/js/ts/api-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ namespace Api {

export interface Match {
players?: Player[];
actionsByPlayerUniqueKey: ActionsByPlayerUniqueKey;
actionsByPlayer: _.Dictionary<Api.PlayerAction>;
playerActions: PlayerAction[];
matchCards: MatchCards;
firstPlayerIndex: number;
roundNumber: number;
currentRound?: Round;
}

export interface ActionsByPlayerUniqueKey extends _.Dictionary<PlayerAction> {
[uniqueKey:string]: PlayerAction;
}

export interface Round {
currentTurnPlayer: Player;
consumedTurns: number;
Expand All @@ -42,21 +38,16 @@ namespace Api {
currentMatch?: Match;
}



interface BasePlayerAction {
player: Player;
}

export interface MatchCards {
board?: Card[];
left: Card[];
byPlayer: MatchCardsByPlayerUniqueKey;
byPlayer: _.Dictionary<Api.PlayerMatchCards>;
}

export interface MatchCardsByPlayerUniqueKey extends _.Dictionary<PlayerMatchCards> {
[uniqueKey:string]: PlayerMatchCards;
}

export interface PlayerMatchCards {
taken: Card[];
Expand Down
22 changes: 11 additions & 11 deletions presentation/web/assets/js/ts/controllers/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace Game {
}

const game: Games.Game = {
hasPlayerMaps: false,
"players": [
{
"name": "Betoven",
Expand Down Expand Up @@ -57,7 +58,7 @@ namespace Game {
"id": 1,
}
],
"actionsByPlayerUniqueKey": {
"actionsByPlayer": {
"1|Betoven": []
},
"playerActions": [],
Expand Down Expand Up @@ -339,11 +340,11 @@ namespace Game {
this.player = $state.params["player"]// || player
this.setGame(this.game)

/*const navPanel = document.getElementById("nav-panel")
navPanel.className = 'visible'
/*const navPanel = document.getElementById("nav-panel")
navPanel.className = 'visible'
const shortHeader = document.getElementById("short-header")
shortHeader.style.display = "flex"*/
const shortHeader = document.getElementById("short-header")
shortHeader.style.display = "flex"*/

this.isClientPlayerGameOwner = Games.isPlayerOwner(this.game, this.player)
this.playerMessage = Games.newMessage(this.game.id,this.player,""); // dev notes: the gameId and playerId are constants but the text (last arg) is set from the UI using ng-model="ctr.playerMessage.text"
Expand Down Expand Up @@ -673,9 +674,9 @@ namespace Game {
})
}

private setGame(game: Games.Game) {
this.game = game;
this.isMatchInProgress = Games.hasMatchInProgress(game)
private setGame(game: Api.Game) {
this.game = Games.setupGamePlayerMaps(game)
this.isMatchInProgress = Games.hasMatchInProgress(this.game)
const currentMatchIndex = _.size(this.game.matchs)
if (this.isMatchInProgress) {
this.currentTurnPlayer = this.game.currentMatch.currentRound.currentTurnPlayer;
Expand Down Expand Up @@ -750,7 +751,7 @@ namespace Game {
}

this.suggestionRequestCount++
const handCards = this.game.currentMatch.matchCards.byPlayer[Players.generateUniqueKey(this.player)].hand
const handCards = this.game.currentMatch.matchCards.byPlayer.get(this.player).hand
const possibleTakeActions = Matchs.Engine.calculatePossibleTakeActions(boardCards, handCards, this.player)
const analizedActions = Matchs.Engine.analizeActions(possibleTakeActions, this.game.currentMatch)
this.possibleTakeActions = analizedActions.possibleActions;
Expand Down Expand Up @@ -789,15 +790,14 @@ namespace Game {
}

private performAnAutomaticDropAction() {
const handCards = this.game.currentMatch.matchCards.byPlayer[Players.generateUniqueKey(this.player)].hand
const handCards = this.game.currentMatch.matchCards.byPlayer.get(this.player).hand
if (handCards.length > 0) {
this.selectedHandCard = handCards[0]
this.performDropAction();
}
}

public isPlayerGameOwner = Games.isPlayerOwner
public playerToUniqueKey = Players.generateUniqueKey // interesting case! both names are pretty same. the player context is provivded by the leading namespace or the inclusion in the name's identifier
public extractPlayerName = Players.extractName
public hasGameStarted = Games.isStarted
}
Expand Down
2 changes: 1 addition & 1 deletion presentation/web/assets/js/ts/services/cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ namespace Rounds {
}

export function isPlayerTurn(round: Round, player: Players.Player): boolean {
return !_.isEmpty(round.currentTurnPlayer) && round.currentTurnPlayer.name == player.name; // TODO: now game players have id, USE ID to avoid a clash!
return !_.isEmpty(round.currentTurnPlayer) && round.currentTurnPlayer.id == player.id;
}
}
93 changes: 60 additions & 33 deletions presentation/web/assets/js/ts/services/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace Games {

export interface Game extends Api.Game { // TODO : analyse if this approach is worty...
export interface Game extends Api.Game {
matchs?: Matchs.Match[]; // previous played matchs
currentMatch?: Matchs.Match;
hasPlayerMaps: boolean;
}

export function hasMatchInProgress(game :Game) : boolean {
Expand All @@ -18,11 +21,7 @@ namespace Games {
}

export function isPlayerOwner(game :Game, player: Players.Player) {
if (Util.isDefined(player.id)) {
return player.id === game.owner.id
} else {
return player.name === game.owner.name // Dev notes (Loop hole here!) : recall that in a game players MUST have different names...
}
return player.id === game.owner.id
}

export function hasPlayerJoin(game :Game, player: Players.Player) {
Expand Down Expand Up @@ -73,32 +72,62 @@ namespace Games {
text: text,
}
}

function isGameWithPlayerMaps(game: Api.Game): game is Game {
return (game as Game).hasPlayerMaps !== undefined;
}

export function setupGamePlayerMaps(game: Api.Game): Game {
if (isGameWithPlayerMaps(game)) {
return game;
}

if (Util.isDefined(game.currentMatch)) {
setupMatchPlayerMaps(game.currentMatch)
}

if (_.size(game.matchs) > 0) {
for (let i = 0; i < game.matchs.length; i++) {
setupMatchPlayerMaps(game.matchs[i])
}
}
return _.extend(game, { hasPlayerMaps : true }) // mark as converted in order to avoid converting again
}

function setupMatchPlayerMaps(match: Api.Match) {
match.actionsByPlayer = <any> new Players.MapByPlayer<Api.PlayerAction>(match.actionsByPlayer)
match.matchCards.byPlayer = <any> new Players.MapByPlayer<Api.PlayerMatchCards>(match.matchCards.byPlayer)
}

export class Service {
constructor(private $http: ng.IHttpService, private $q: ng.IQService) {
}

// TODO: remove Game or Games letters from these methods, as Games. provides already context (avoid slutering)
getGames(): ng.IPromise<Game[]> {
return this.$http.get<Game[]>(`/api/v1/games`).then((response) => {
return response.data;
return this.$http.get<Api.Game[]>(`/api/v1/games`).then((response) => {
const games = response.data
return _.map(games,(game) => setupGamePlayerMaps(game))
});
}

getGameById(gamesId: number): ng.IPromise<Game> {
return this.$http.get<Game>(`/api/v1/games/${gamesId}`).then((response) => {
return response.data;
return this.$http.get<Api.Game>(`/api/v1/games/${gamesId}`).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
});
}

createGame(game: Game): ng.IPromise<Game> {
return this.$http.post<Game>(`/api/v1/games`,game).then((response) => {
return response.data
createGame(game: Api.Game): ng.IPromise<Game> {
return this.$http.post<Api.Game>(`/api/v1/games`,game).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}

/*updateGame(game: Game): ng.IPromise<Game> {
return this.$http.put<Game>(`/api/v1/games/${game.id}`,game).then((response) => {
return response.data
return this.$http.put<Api.Game>(`/api/v1/games/${game.id}`,game).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}*/

Expand All @@ -112,26 +141,30 @@ namespace Games {
}

startGame(game: Game): ng.IPromise<Game> {
return this.$http.post<Game>(`/api/v1/games/${game.id}/start`, undefined).then((response) => {
return response.data
return this.$http.post<Api.Game>(`/api/v1/games/${game.id}/start`,game).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}

joinGame(game: Game): ng.IPromise<Game> {
return this.$http.post<Game>(`/api/v1/games/${game.id}/join`,undefined).then((response) => {
return response.data
return this.$http.post<Api.Game>(`/api/v1/games/${game.id}/join`,undefined).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}

quitGame(game: Game): ng.IPromise<Game> {
return this.$http.post<Game>(`/api/v1/games/${game.id}/quit`,undefined).then((response) => {
return response.data
return this.$http.post<Api.Game>(`/api/v1/games/${game.id}/quit`,undefined).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}

addComputerPlayer(game: Game): ng.IPromise<Game> {
return this.$http.post<Game>(`/api/v1/games/${game.id}/add-computer`,undefined).then((response) => {
return response.data
return this.$http.post<Api.Game>(`/api/v1/games/${game.id}/add-computer`,undefined).then((response) => {
const game = response.data
return setupGamePlayerMaps(game)
})
}

Expand Down Expand Up @@ -159,21 +192,15 @@ namespace Games {
}

bindWebSocket(id: number) {
return this.$http.get<Game>(`/api/v1/games/${id}/bind-ws`).then((response) => {
return response.data;
});
return this.$http.get(`/api/v1/games/${id}/bind-ws`)
}

unbindWebSocket(id: number) {
return this.$http.get<Game>(`/api/v1/games/${id}/unbind-ws`).then((response) => {
return response.data;
});
return this.$http.get(`/api/v1/games/${id}/unbind-ws`)
}

sendMessage(msg: VolatileMessage) {
return this.$http.post<Game>(`/api/v1/games/${msg.gameId}/message`, msg).then((response) => {
return response.data
})
return this.$http.post<void>(`/api/v1/games/${msg.gameId}/message`, msg)
}
}

Expand Down
9 changes: 9 additions & 0 deletions presentation/web/assets/js/ts/services/matchs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@

namespace Matchs {

export interface Match extends Api.Match {
actionsByPlayer: Players.MapByPlayer<Api.PlayerAction> & _.Dictionary<Api.PlayerAction>; // dev notes: kind of odd as it should be Players.MapByPlayer<Api.PlayerAction>, but it must be backward compatible with the extended type Api.Match and the field actionsByPlayer can be a _.Dictionary
matchCards: MatchCards;
}

export interface MatchCards extends Api.MatchCards {
byPlayer: Players.MapByPlayer<Api.PlayerMatchCards> & _.Dictionary<Api.PlayerMatchCards>; // dev notes: the same of above but with different types
}

export namespace Rules {
function sumValues(cards :Api.Card[]){
const total = _.reduce(cards,(acc,card) => {
Expand Down
Loading

0 comments on commit 2ed1645

Please sign in to comment.