Skip to content

Commit 43c9b44

Browse files
Passing and resigning implemented
The user can pass during a game and resign, conceding defeat. Additionally, when the user runs out of time on their clock, the loss on time is now acknowledge in accordance with the API specifications.
1 parent d263130 commit 43c9b44

File tree

12 files changed

+301
-35
lines changed

12 files changed

+301
-35
lines changed

controllers/channels/GameChannel.ts

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Controllers {
44
export class GameChannel extends ChannelBase {
55
private _board: Views.GoBoard;
6+
private _userColour: Models.GameStone;
67

78
constructor(parent: ChannelController, channelId: number) {
89
super(parent, channelId);
@@ -37,14 +38,15 @@ namespace Controllers {
3738
}
3839

3940
let gameChannel = this.channel as Models.GameChannel;
40-
let userColour: Models.GameStone;
4141
if (gameChannel.playerWhite == this.database.username)
42-
userColour = Models.GameStone.White;
42+
this._userColour = Models.GameStone.White;
4343
else if (gameChannel.playerBlack == this.database.username)
44-
userColour = Models.GameStone.Black;
44+
this._userColour = Models.GameStone.Black;
45+
else
46+
this._userColour = null;
4547

4648
if (updateOverlay) {
47-
this._board.updateOverlay(gameChannel, userColour);
49+
this._board.updateOverlay(gameChannel, this._userColour);
4850
}
4951

5052
let splitKomi = gameState.rules.splitKomi();
@@ -69,38 +71,68 @@ namespace Controllers {
6971
home = temp;
7072
}
7173

72-
this._board.playerHome.update(home.colour, home.clock, home.prisoners, home.komi, home.user);
74+
if (this._userColour != null) {
75+
let passCallback: Function = ((gameChannel.phase == Models.GamePhase.Active) && (gameChannel.hasAction(Models.GameActions.Move)))? this._passCallback : null;
76+
let resignCallback: Function = (gameChannel.phase != Models.GamePhase.Concluded)? this._resignCallback : null;
77+
this._board.playerHome.update(home.colour, home.clock, home.prisoners, home.komi, home.user, true, passCallback, resignCallback);
78+
}
79+
else {
80+
this._board.playerHome.update(home.colour, home.clock, home.prisoners, home.komi, home.user);
81+
}
82+
7383
this._board.playerAway.update(away.colour, away.clock, away.prisoners, away.komi, away.user);
7484
}
7585
}
7686

7787
private tryPlay(x: number, y: number): boolean {
7888
let gameChannel = this.channel as Models.GameChannel;
79-
if (!gameChannel.hasAction(Models.GameActions.Move)) {
80-
console.log("move action not available");
81-
return false;
82-
}
89+
if (!gameChannel.hasAction(Models.GameActions.Move)) return false;
8390

8491
let gameState = this.database.games[this.channelId];
85-
if ((!gameState) || (!gameState.tree)) return;
86-
87-
let r = gameState.tree.tryPlay(x, y);
88-
switch (r as Models.GameMoveError) {
89-
case Models.GameMoveError.Success:
90-
gameChannel.disableAction(Models.GameActions.Move);
91-
this.client.post(<KGS.Upstream.GAME_MOVE>{
92-
type: KGS.Upstream._GAME_MOVE,
93-
channelId: this.channelId,
94-
x: x,
95-
y: y
96-
});
97-
return true;
98-
99-
case Models.GameMoveError.InvalidLocation: console.log("given coordinates are not on board"); return false;
100-
case Models.GameMoveError.StonePresent: console.log("on given coordinates already is a stone"); return false;
101-
case Models.GameMoveError.Suicide: console.log("suicide (currently they are forbbiden)"); return false;
102-
case Models.GameMoveError.Ko: console.log("ko ko ko"); return false;
103-
default: console.log("unknown outcome"); return false;
92+
if ((!gameState) || (!gameState.tree)) return false;
93+
94+
if ((x != null) && (y != null)) {
95+
let r = gameState.tree.tryPlay(x, y);
96+
switch (r as Models.GameMoveError) {
97+
case Models.GameMoveError.Success:
98+
gameChannel.disableAction(Models.GameActions.Move);
99+
this.client.post(<KGS.Upstream.GAME_MOVE>{
100+
type: KGS.Upstream._GAME_MOVE,
101+
channelId: this.channelId,
102+
x: x,
103+
y: y
104+
});
105+
return true;
106+
107+
case Models.GameMoveError.InvalidLocation: console.log("given coordinates are not on board"); return false;
108+
case Models.GameMoveError.StonePresent: console.log("on given coordinates already is a stone"); return false;
109+
case Models.GameMoveError.Suicide: console.log("suicide (currently they are forbbiden)"); return false;
110+
case Models.GameMoveError.Ko: console.log("ko ko ko"); return false;
111+
default: console.log("unknown outcome"); return false;
112+
}
113+
}
114+
else {
115+
gameChannel.disableAction(Models.GameActions.Move);
116+
this.client.post(<KGS.Upstream.GAME_MOVE>{
117+
type: KGS.Upstream._GAME_MOVE,
118+
channelId: this.channelId,
119+
});
120+
return true;
121+
}
122+
}
123+
124+
private _passCallback = () => {
125+
this.tryPlay(undefined, undefined);
126+
}
127+
private _resignCallback = () => {
128+
if (this._userColour != null) {
129+
let gameChannel = this.channel as Models.GameChannel;
130+
gameChannel.disableAction(Models.GameActions.Move);
131+
132+
this.client.post(<KGS.Upstream.GAME_RESIGN>{
133+
type: KGS.Upstream._GAME_RESIGN,
134+
channelId: this.channelId,
135+
});
104136
}
105137
}
106138
}

kgs/JSONClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ namespace KGS {
134134

135135
case KGS.Downstream._LOGOUT:
136136
return this.logoutReceived();
137+
138+
case KGS.Downstream._GAME_TIME_EXPIRED:
139+
this.kgsPOST(message);
140+
return false;
137141
}
138142

139143
return true;

kgs/protocol/Downstream.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ namespace KGS {
185185
export interface GAME_OVER extends ChannelMessage, GameScore {
186186
}
187187

188+
export const _GAME_TIME_EXPIRED: string = "GAME_TIME_EXPIRED";
189+
export interface GAME_TIME_EXPIRED extends ChannelMessage {
190+
}
191+
188192
export const _CHALLENGE_PROPOSAL: string = "CHALLENGE_PROPOSAL";
189193
export interface CHALLENGE_PROPOSAL extends ChannelMessage {
190194
proposal: KGS.DownstreamProposal

kgs/protocol/Upstream.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,8 @@ namespace KGS {
6969
x?: number,
7070
y?: number
7171
}
72+
export const _GAME_RESIGN: string = "GAME_RESIGN";
73+
export interface GAME_RESIGN extends ChannelMessage {
74+
}
7275
}
7376
}

models/GameResult.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ namespace Models {
100100

101101
if (byLine) {
102102
if (userColour != null)
103-
return ((this.victor == userColour)? "Victory" : "Defeat") + byLine;
103+
return ((this.victor == userColour)? "victory" : "defeat") + byLine;
104104
else
105105
return ((this.victor == Models.GameStone.White)? (whiteName || "White") : (blackName || "Black")) + " won" + byLine;
106106
}

scss/_font-awesome.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@
3434
.fa-circle:before { @extend .fa; content: "\f111"; }
3535
.fa-flag-checkered:before { @extend .fa; content: "\f11e"; }
3636
.fa-graduation-cap:before { @extend .fa; content: "\f19d"; }
37+
.fa-thumbs-up:before { @extend .fa; content: "\f164"; }
38+
.fa-thumbs-down:before { @extend .fa; content: "\f165"; }
3739
.fa-commenting:before { @extend .fa; content: "\f27a"; }

views/go-board-player/GoBoardPlayer.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,17 @@ $view-go-board-player-padding: 10px;
9393
height: $view-go-clock-height;
9494
border-radius: $border-radius;
9595
}
96+
97+
.go-board-player .player-buttons {
98+
display: block;
99+
overflow: hidden;
100+
}
101+
.go-board-player .player-buttons.hidden {
102+
display: none;
103+
}
104+
.go-board-player .player-buttons .safety-button + .safety-button {
105+
margin-top: 3px;
106+
}
107+
.go-board-player .player-buttons .safety-button .primary {
108+
width: 70px;
109+
}

views/go-board-player/GoBoardPlayer.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ namespace Views {
1414
private _komiBase: HTMLSpanElement;
1515
private _komiHalf: HTMLSpanElement;
1616

17+
private _buttonsDiv: HTMLDivElement;
18+
private _buttonPass: Views.SafetyButton;
19+
private _buttonResign: Views.SafetyButton;
20+
1721
constructor(playerTeam: Models.PlayerTeam) {
1822
super(Views.Templates.cloneTemplate<HTMLDivElement>('go-board-player'));
1923

@@ -24,6 +28,15 @@ namespace Views {
2428

2529
if (playerTeam == Models.PlayerTeam.Home) {
2630
this._clock.attach(playerStats, false);
31+
32+
this._buttonsDiv = Templates.createDiv('player-buttons hidden');
33+
playerStats.insertBefore(this._buttonsDiv, playerStats.firstElementChild);
34+
35+
this._buttonPass = new Views.SafetyButton("pass", false);
36+
this._buttonPass.attach(this._buttonsDiv);
37+
38+
this._buttonResign = new Views.SafetyButton("resign", true);
39+
this._buttonResign.attach(this._buttonsDiv);
2740
}
2841
else {
2942
this.root.insertBefore(playerStats, playerInfo);
@@ -52,7 +65,7 @@ namespace Views {
5265
this._clock.deactivate();
5366
}
5467

55-
public update(colour: Models.GameStone, clock: Models.GameClock, prisoners: number, komi: { base: number, half?: boolean }, user: Models.User) {
68+
public update(colour: Models.GameStone, clock: Models.GameClock, prisoners: number, komi: { base: number, half?: boolean }, user: Models.User, showButtons?: boolean, passCallback?: Function, resignCallback?: Function) {
5669
// Player Colour
5770
if (colour == Models.GameStone.White) {
5871
this._userStone.src = 'img/stone-white.png';
@@ -101,6 +114,22 @@ namespace Views {
101114
this._userName.innerHTML = "&nbsp;";
102115
this._userRank.innerHTML = "&nbsp;";
103116
}
117+
118+
// Player Buttons
119+
if (this._buttonsDiv) {
120+
if (showButtons) {
121+
this._buttonsDiv.classList.remove('hidden');
122+
if (this._buttonPass) {
123+
this._buttonPass.callback = passCallback;
124+
this._buttonPass.disabled = (passCallback == null);
125+
}
126+
if (this._buttonResign) {
127+
this._buttonResign.callback = resignCallback;
128+
this._buttonResign.disabled = (resignCallback == null);
129+
}
130+
}
131+
else this._buttonsDiv.classList.add('hidden');
132+
}
104133
}
105134
}
106135
}

views/go-board/GoBoard.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,29 @@ namespace Views {
173173
this._resultOverlay.classList.add('hidden');
174174
}
175175
else {
176-
this._resultOverlay.classList.remove('hidden');
176+
let className = 'result-overlay';
177177
let headline: string;
178178

179179
switch (gameChannel.phase) {
180180
case Models.GamePhase.Adjourned: headline = "Adjourned"; break;
181181
case Models.GamePhase.Paused: headline = "Paused"; break;
182182
default:
183-
if (gameChannel.result)
183+
if (gameChannel.result) {
184184
headline = gameChannel.result.getHeadline(userColour, gameChannel.playerWhite, gameChannel.playerBlack);
185-
else
186-
headline = "unknown result";
187-
185+
if ((userColour != null) && (gameChannel.result.victor != null)) {
186+
if (gameChannel.result.victor == userColour) {
187+
className += ' victory';
188+
}
189+
else {
190+
className += ' defeat';
191+
}
192+
}
193+
}
194+
else headline = "unknown result";
188195
break;
189196
}
190197

198+
this._resultOverlay.className = className;
191199
this._resultHeading.innerText = headline;
192200
}
193201
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<section data-view="safety-button">
2+
<div class="safety-button">
3+
<div class="primary" title=""></div>
4+
<div class="confirmation" title=""></div>
5+
</div>
6+
</section>

0 commit comments

Comments
 (0)