201 lines
6.6 KiB
MonkeyC
201 lines
6.6 KiB
MonkeyC
|
|
import Toybox.Lang;
|
||
|
|
|
||
|
|
// Teams
|
||
|
|
const US = 0;
|
||
|
|
const THEM = 1;
|
||
|
|
|
||
|
|
// Court quadrants (your team = bottom half)
|
||
|
|
enum {
|
||
|
|
QUAD_BOTTOM_RIGHT, // US, deuce (right)
|
||
|
|
QUAD_BOTTOM_LEFT, // US, ad (left)
|
||
|
|
QUAD_UPPER_LEFT, // THEM, deuce (their right -> screen left)
|
||
|
|
QUAD_UPPER_RIGHT // THEM, ad (their left -> screen right)
|
||
|
|
}
|
||
|
|
|
||
|
|
class PadelMatch {
|
||
|
|
private var _rules as RulesConfig;
|
||
|
|
|
||
|
|
private var _points as Array<Number>; // raw point count per team in the current game
|
||
|
|
private var _games as Array<Number>; // games won in the current set
|
||
|
|
private var _sets as Array<Number>; // sets won in the match
|
||
|
|
private var _server as Number; // team serving the current game
|
||
|
|
private var _inTiebreak as Boolean;
|
||
|
|
private var _tbPoints as Array<Number>;
|
||
|
|
private var _tbFirstServer as Number; // team that served the tiebreak's first point
|
||
|
|
private var _matchOver as Boolean;
|
||
|
|
private var _winner as Number; // US / THEM / -1
|
||
|
|
private var _history as Array<Dictionary>;
|
||
|
|
|
||
|
|
private const POINT_LABELS = ["0", "15", "30", "40"];
|
||
|
|
|
||
|
|
public function initialize(rules as RulesConfig) {
|
||
|
|
_rules = rules;
|
||
|
|
_points = [0, 0];
|
||
|
|
_games = [0, 0];
|
||
|
|
_sets = [0, 0];
|
||
|
|
_server = US;
|
||
|
|
_inTiebreak = false;
|
||
|
|
_tbPoints = [0, 0];
|
||
|
|
_tbFirstServer = US;
|
||
|
|
_matchOver = false;
|
||
|
|
_winner = -1;
|
||
|
|
_history = [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---- public API ----
|
||
|
|
|
||
|
|
public function pointTo(team as Number) as Void {
|
||
|
|
if (_matchOver) { return; }
|
||
|
|
_pushHistory();
|
||
|
|
if (_inTiebreak) {
|
||
|
|
_tbPoints[team] += 1;
|
||
|
|
_resolveTiebreak(team);
|
||
|
|
} else {
|
||
|
|
_points[team] += 1;
|
||
|
|
_resolveGame(team);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function undo() as Void {
|
||
|
|
var n = _history.size();
|
||
|
|
if (n == 0) { return; }
|
||
|
|
var s = _history[n - 1];
|
||
|
|
_history = _history.slice(0, n - 1);
|
||
|
|
_points = s["points"] as Array<Number>;
|
||
|
|
_games = s["games"] as Array<Number>;
|
||
|
|
_sets = s["sets"] as Array<Number>;
|
||
|
|
_server = s["server"] as Number;
|
||
|
|
_inTiebreak = s["inTiebreak"] as Boolean;
|
||
|
|
_tbPoints = s["tbPoints"] as Array<Number>;
|
||
|
|
_tbFirstServer = s["tbFirstServer"] as Number;
|
||
|
|
_matchOver = s["matchOver"] as Boolean;
|
||
|
|
_winner = s["winner"] as Number;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function canSetServer() as Boolean {
|
||
|
|
return _history.size() == 0 && !_matchOver;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function toggleStartingServer() as Void {
|
||
|
|
if (canSetServer()) { _server = 1 - _server; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public function servingQuadrant() as Number {
|
||
|
|
var team;
|
||
|
|
var pointsPlayed;
|
||
|
|
if (_inTiebreak) {
|
||
|
|
pointsPlayed = _tbPoints[0] + _tbPoints[1];
|
||
|
|
team = _tiebreakServer(pointsPlayed);
|
||
|
|
} else {
|
||
|
|
pointsPlayed = _points[0] + _points[1];
|
||
|
|
team = _server;
|
||
|
|
}
|
||
|
|
var isDeuce = (pointsPlayed % 2) == 0;
|
||
|
|
return _quadrantFor(team, isDeuce);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function pointLabel(team as Number) as String {
|
||
|
|
if (_inTiebreak) { return _tbPoints[team].toString(); }
|
||
|
|
var a = _points[team];
|
||
|
|
var b = _points[1 - team];
|
||
|
|
if (!_rules.goldenPoint && a == 4 && b == 3) { return "AD"; }
|
||
|
|
if (a >= 4) { return "40"; } // safety; game normally ends before display
|
||
|
|
return POINT_LABELS[a];
|
||
|
|
}
|
||
|
|
|
||
|
|
public function games(team as Number) as Number { return _games[team]; }
|
||
|
|
public function sets(team as Number) as Number { return _sets[team]; }
|
||
|
|
public function server() as Number { return _server; }
|
||
|
|
public function isInTiebreak() as Boolean { return _inTiebreak; }
|
||
|
|
public function isMatchOver() as Boolean { return _matchOver; }
|
||
|
|
public function winner() as Number { return _winner; }
|
||
|
|
public function tiebreakPoints(team as Number) as Number { return _tbPoints[team]; }
|
||
|
|
|
||
|
|
// ---- internals ----
|
||
|
|
|
||
|
|
private function _resolveGame(team as Number) as Void {
|
||
|
|
var other = 1 - team;
|
||
|
|
var a = _points[team];
|
||
|
|
var b = _points[other];
|
||
|
|
if (_rules.goldenPoint) {
|
||
|
|
if (a >= 4) { _winGame(team); }
|
||
|
|
} else {
|
||
|
|
if (a >= 4 && (a - b) >= 2) { _winGame(team); }
|
||
|
|
else if (a >= 4 && b >= 4) { _points[team] = 3; _points[other] = 3; } // both past 40 -> deuce
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _winGame(team as Number) as Void {
|
||
|
|
_games[team] += 1;
|
||
|
|
_points = [0, 0];
|
||
|
|
_server = 1 - _server;
|
||
|
|
_resolveSet(team);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _resolveSet(team as Number) as Void {
|
||
|
|
var other = 1 - team;
|
||
|
|
var a = _games[team];
|
||
|
|
var b = _games[other];
|
||
|
|
if (a >= _rules.gamesPerSet && (a - b) >= 2) {
|
||
|
|
_winSet(team);
|
||
|
|
} else if (_rules.tiebreakEnabled && a == _rules.gamesPerSet && b == _rules.gamesPerSet) {
|
||
|
|
_inTiebreak = true;
|
||
|
|
_tbPoints = [0, 0];
|
||
|
|
_tbFirstServer = _server; // team due to serve after the 12th game
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _resolveTiebreak(team as Number) as Void {
|
||
|
|
var other = 1 - team;
|
||
|
|
var a = _tbPoints[team];
|
||
|
|
var b = _tbPoints[other];
|
||
|
|
if (a >= _rules.tiebreakTarget && (a - b) >= 2) {
|
||
|
|
_games[team] += 1; // e.g. 7-6
|
||
|
|
_winSet(team);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _winSet(team as Number) as Void {
|
||
|
|
_sets[team] += 1;
|
||
|
|
_games = [0, 0];
|
||
|
|
if (_inTiebreak) {
|
||
|
|
_inTiebreak = false;
|
||
|
|
_server = 1 - _tbFirstServer; // tiebreak receiver serves first next set
|
||
|
|
}
|
||
|
|
_resolveMatch(team);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _resolveMatch(team as Number) as Void {
|
||
|
|
if (_sets[team] >= _rules.setsToWin) {
|
||
|
|
_matchOver = true;
|
||
|
|
_winner = team;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _tiebreakServer(pointsPlayed as Number) as Number {
|
||
|
|
var offset = ((pointsPlayed + 1) / 2) % 2; // integer division
|
||
|
|
return (offset == 0) ? _tbFirstServer : (1 - _tbFirstServer);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _quadrantFor(team as Number, isDeuce as Boolean) as Number {
|
||
|
|
if (team == US) {
|
||
|
|
return isDeuce ? QUAD_BOTTOM_RIGHT : QUAD_BOTTOM_LEFT;
|
||
|
|
}
|
||
|
|
return isDeuce ? QUAD_UPPER_LEFT : QUAD_UPPER_RIGHT;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function _pushHistory() as Void {
|
||
|
|
_history.add({
|
||
|
|
"points" => [_points[0], _points[1]],
|
||
|
|
"games" => [_games[0], _games[1]],
|
||
|
|
"sets" => [_sets[0], _sets[1]],
|
||
|
|
"server" => _server,
|
||
|
|
"inTiebreak" => _inTiebreak,
|
||
|
|
"tbPoints" => [_tbPoints[0], _tbPoints[1]],
|
||
|
|
"tbFirstServer" => _tbFirstServer,
|
||
|
|
"matchOver" => _matchOver,
|
||
|
|
"winner" => _winner
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|