/*
* bawo.zone - <a href="https://bawo.zone">https://bawo.zone</a>
* <a href="https://github.com/fumba/bawo.zone">https://github.com/fumba/bawo.zone</a>
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import AppConstants from "./AppConstants";
import Board from "./Board";
import MoveDirection from "./MoveDirection";
import Player from "./Player";
import Utility from "../Utility";
import { isEmpty } from "lodash";
import HoleUI from "../ui_entities/HoleUI";
import SeedUI from "../ui_entities/SeedUI";
import SeedGroupUI from "../ui_entities/SeedGroupUI";
import Move from "./Move";
import UiTaskActions from "./UiTaskActions";
class Hole {
// Number of seeds in hole
public numSeeds: number;
// The next hole
public nextHole: Hole;
// Previous hole
public prevHole: Hole;
// Hole ID (based on PlayerBoardHoles)
public readonly id: number;
// Unique hole ID (with player side)
public readonly UID: string;
// The player who is assigned this hole
public readonly player: Player;
// Whether the player is allowed to make moves on this hole.
public moveStatus: MoveDirection;
// the board on which the hole is placed
public board: Board;
// Hole UI corresponding to this hole
public ui: HoleUI;
// Seed Group UI corresponding to this hole
public seedGroupUI: SeedGroupUI;
/**
* Constructor
*
* @param {Player} player The player to whom this hole is being assigned to
* @param {Board} board The board on which the hole is placed
* @param {number} id Unique hole ID (Ranging 0-16) -1 is used for the dummy
* pointer to the first hole.
* @param {number} numSeeds Number of seeds to be initially added to the hole.
* @param {Hole} prevHole The hole that is before this hole.
* @param {Hole} nextHole The hole that comes up next in clockwise fashion
*/
constructor(
player: Player,
board: Board,
id: number,
numSeeds: number,
prevHole: Hole,
nextHole: Hole
) {
this.player = player;
this.id = id;
if (!isEmpty(this.player)) {
this.UID = `[${Utility.padZero(this.id)}-${this.player.side}]`;
}
this.numSeeds = numSeeds;
this.nextHole = nextHole;
this.prevHole = prevHole;
this.board = board;
// initialize move status as unauthorized
this.moveStatus = MoveDirection.UnAuthorized;
}
/**
* Check if this hole is in the front row (Front rows are allowed to capture
* seeds from enemy side).
*
* @returns {boolean} true is the hole is in the front row for the owning player.
*/
public isInFrontRow(): boolean {
if (this.player.isOnTopSide()) {
return this.id >= 8;
} else {
return this.id < 8;
}
}
/**
* Checks to see if the hole contains any seeds
*
* @returns {boolean} true if seeds are present
*/
public isEmpty(): boolean {
return this.numSeeds == 0;
}
public toString(): string {
return `[${Utility.padZero(this.id)}(${Utility.padZero(this.numSeeds)})-${
this.moveStatus
}]`;
}
/**
* Moves all seeds from the hole into players hand - clearing it to a count of 0.
*
* @returns {number} The number of seeds that were moved from hole into players hand.
*/
public transferAllSeedsToCurrPlayer(): number {
if (this.numSeeds == 0) {
return 0;
}
const numSeedsTemp = this.numSeeds;
this.numSeeds = 0;
if (this.board.isInGraphicsMode()) {
const task = {
name: UiTaskActions.GRAB_ALL_SEEDS_FROM_HOLE,
hole: this,
isCapture: this.player.side != this.board.getCurrentPlayer().side,
};
this.board.uiTaskQueue.enqueue(task);
}
if (numSeedsTemp > 0) {
this.board.getCurrentPlayer().numSeedsInHand += numSeedsTemp;
}
return numSeedsTemp;
}
/**
* Removes seeds from players hand
*
* @param {number} numSeeds Number of seeds to be removed from players hand
* @returns {number} Number of seeds removed from the player hand
*/
public transferSeedsFromCurrPlayer(numSeeds: number): number {
this.validateNumSeeds(numSeeds);
this.validateFinalSeedCount(-numSeeds); //minus because we are removing seeds
this.board.getCurrentPlayer().numSeedsInHand -= numSeeds;
this.numSeeds += numSeeds;
if (this.board.isInGraphicsMode()) {
//add seeds to hole ui
for (let i = 0; i < numSeeds; i++) {
const task = {
name: UiTaskActions.SOW_SEED_INTO_HOLE,
seedId: SeedUI.seedGroupId(this.UID),
seedGroupUI: this.seedGroupUI,
};
this.board.uiTaskQueue.enqueue(task);
}
}
return numSeeds;
}
/**
* Checks if the number of seeds to be added or removed from players hand is valid
*
* @param {number} numSeeds Number of seeds to be added or removed from players hand
*/
private validateNumSeeds(numSeeds: number): void {
let message: string = null;
if (numSeeds < 0) {
message =
"Attempted to add or remove negative number seeds | input : " +
numSeeds;
} else if (numSeeds == 0) {
message = "Attempted to add or remove no seeds";
}
if (message) {
throw new Error(message);
}
}
/**
* Checks if the the total number of seeds in players hand is valid
*
* @param {number} numSeeds Number of seeds in players hand after adding or removing
*/
private validateFinalSeedCount(numSeeds: number): void {
let message: string = null;
if (this.board.getCurrentPlayer().numSeedsInHand + numSeeds < 0) {
message =
"Total number of seeds after operation is negative | input: " +
numSeeds;
}
if (message) {
throw new Error(message);
}
}
/**
* Gets the moves that are available for the current player
*
* @returns {Array<Move>} moves that can be played
*/
public availableMovesForCurrentPlayer(): Array<Move> {
const moves: Array<Move> = [];
if (
this.moveStatus == MoveDirection.Clockwise ||
this.moveStatus == MoveDirection.AntiClockwise
) {
moves.push(new Move(this, this.moveStatus));
} else if (this.moveStatus == MoveDirection.Both) {
moves.push(new Move(this, MoveDirection.Clockwise));
moves.push(new Move(this, MoveDirection.AntiClockwise));
}
return moves;
}
/**
* Calculates the move direction that corresponds to a users drag and drop action.
* Seeds are dragged from an origin hole into the target hole. The direction is only returned if
* the target hole is adjacent to the origin.
*
* @param {Hole} targetHole the hole into which the seeds are being dragged into
* @returns {MoveDirection} the direction that corresponds to the users drag and drop action.
*/
public adjacencyDirection(targetHole: Hole): MoveDirection {
//retrieved valid move directions for the hole
const validDirections = this.availableMovesForCurrentPlayer().map(
(move) => move.direction
);
if (
validDirections.includes(MoveDirection.Clockwise) &&
this.nextHole.UID == targetHole.UID
) {
return MoveDirection.Clockwise;
} else if (
validDirections.includes(MoveDirection.AntiClockwise) &&
this.prevHole.UID == targetHole.UID
) {
return MoveDirection.AntiClockwise;
}
return null;
}
/**
* Renders the hole and its contents (seeds)
*/
public renderUI(): void {
//render hole
const holeUI = this.board.me.pool.pull(AppConstants.HOLE_UI, this);
this.board.ui.addChild(holeUI);
this.ui = holeUI;
// invisible draggable collection that contains seeds
const seedGroup = this.board.me.pool.pull(AppConstants.SEED_GROUP_UI, this);
this.seedGroupUI = seedGroup;
this.board.ui.addChild(seedGroup);
for (let i = 0; i < this.numSeeds; i++) {
// render seeds that belong to hole
const seedUI = this.board.me.pool.pull(
AppConstants.SEED_UI,
this.seedGroupUI,
this.UID
);
this.board.ui.addChild(seedUI);
}
}
}
export default Hole;
Source