chore: code reorganized
This commit is contained in:
		
							
								
								
									
										570
									
								
								src/game/Board.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										570
									
								
								src/game/Board.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,570 @@
 | 
			
		||||
import {
 | 
			
		||||
  Application,
 | 
			
		||||
  Assets,
 | 
			
		||||
  Container,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Graphics,
 | 
			
		||||
  Sprite,
 | 
			
		||||
  Text,
 | 
			
		||||
  Ticker
 | 
			
		||||
} from 'pixi.js'
 | 
			
		||||
import { Scale, type ScaleFunction } from '@/game/utilities/scale'
 | 
			
		||||
import type { GameState, Movement, TileDto } from '@/common/interfaces'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import { DIRECTIONS, createContainer, isTilePair } from '@/common/helpers'
 | 
			
		||||
import { createText } from '@/game/utilities/fonts'
 | 
			
		||||
import { Dot } from '@/game/Dot'
 | 
			
		||||
 | 
			
		||||
export class Board extends EventEmitter {
 | 
			
		||||
  private _scale: number = 1
 | 
			
		||||
  private _canMove: boolean = false
 | 
			
		||||
 | 
			
		||||
  ticker: Ticker
 | 
			
		||||
  height: number
 | 
			
		||||
  width: number
 | 
			
		||||
  grain: number = 25
 | 
			
		||||
  scaleY: ScaleFunction
 | 
			
		||||
  scaleX: ScaleFunction
 | 
			
		||||
  state: GameState | undefined
 | 
			
		||||
  container!: Container
 | 
			
		||||
  initialContainer!: Container
 | 
			
		||||
  tilesContainer!: Container
 | 
			
		||||
  leftContainer!: Container
 | 
			
		||||
  rightContainer!: Container
 | 
			
		||||
  interactionContainer!: Container
 | 
			
		||||
  textContainer!: Container
 | 
			
		||||
  tiles: Tile[] = []
 | 
			
		||||
  boneyard: Tile[] = []
 | 
			
		||||
  movements: any[] = []
 | 
			
		||||
  freeEnds?: [number, number]
 | 
			
		||||
  leftTile?: Tile
 | 
			
		||||
  rightTile?: Tile
 | 
			
		||||
  nextTile?: Tile
 | 
			
		||||
  textWaitForPlayers!: Text
 | 
			
		||||
  textYourTurn!: Text
 | 
			
		||||
  leftDirection: string = 'west'
 | 
			
		||||
  rightDirection: string = 'east'
 | 
			
		||||
  playerHand: Tile[] = []
 | 
			
		||||
  firstTile: Tile | undefined
 | 
			
		||||
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
    this.ticker = app.ticker
 | 
			
		||||
    this.height = app.canvas.height - 130
 | 
			
		||||
    this.width = app.canvas.width
 | 
			
		||||
    this.scaleX = Scale([0, this.width], [0, this.width])
 | 
			
		||||
    this.scaleY = Scale([0, this.height], [0, this.height])
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
 | 
			
		||||
    this.container = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: app.stage
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const background = new Sprite(Assets.get('bg-1'))
 | 
			
		||||
    background.width = this.width
 | 
			
		||||
    background.height = this.height
 | 
			
		||||
    this.container.addChild(background)
 | 
			
		||||
 | 
			
		||||
    this.initialContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      // color: 0x1e2f23,
 | 
			
		||||
      visible: false,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.tilesContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      // color: 0x1e2f23,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.interactionContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
      visible: false
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const verticalLine = new Graphics()
 | 
			
		||||
      .moveTo(this.scaleX(0), 0)
 | 
			
		||||
      .lineTo(this.scaleX(0), this.height)
 | 
			
		||||
      .stroke(0xff0000)
 | 
			
		||||
    const horizontalLine = new Graphics()
 | 
			
		||||
      .moveTo(0, this.scaleY(0))
 | 
			
		||||
      .lineTo(this.width, this.scaleY(0))
 | 
			
		||||
      .stroke(0xff0000)
 | 
			
		||||
    verticalLine.alpha = 0.2
 | 
			
		||||
    horizontalLine.alpha = 0.2
 | 
			
		||||
    this.tilesContainer.addChild(verticalLine)
 | 
			
		||||
    this.tilesContainer.addChild(horizontalLine)
 | 
			
		||||
 | 
			
		||||
    this.createTexts()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private calculateScale() {
 | 
			
		||||
    const scaleXSteps = Math.floor(this.width / (this.grain * this.scale)) / 2
 | 
			
		||||
    const scaleYSteps = Math.floor(this.height / (this.grain * this.scale)) / 2
 | 
			
		||||
    this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
 | 
			
		||||
    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get count() {
 | 
			
		||||
    return this.tiles.length
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get scale() {
 | 
			
		||||
    return this._scale
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set scale(value: number) {
 | 
			
		||||
    this._scale = value
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get canMove() {
 | 
			
		||||
    return this._canMove
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set canMove(value: boolean) {
 | 
			
		||||
    this._canMove = value
 | 
			
		||||
    this.updateCanMoveText()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPlayerHand(tiles: Tile[]) {
 | 
			
		||||
    this.playerHand = tiles
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTexts() {
 | 
			
		||||
    this.textContainer = new Container()
 | 
			
		||||
    this.textWaitForPlayers = createText('Waiting for players', this.scaleX(0), 100)
 | 
			
		||||
    this.textYourTurn = createText('Your turn!', this.scaleX(0), 100)
 | 
			
		||||
 | 
			
		||||
    this.container.addChild(this.textContainer)
 | 
			
		||||
    this.textContainer.addChild(this.textWaitForPlayers)
 | 
			
		||||
    this.textContainer.addChild(this.textYourTurn)
 | 
			
		||||
    this.textYourTurn.visible = false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateCanMoveText() {
 | 
			
		||||
    this.textWaitForPlayers.visible = !this.canMove
 | 
			
		||||
    this.textYourTurn.visible = this.canMove
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setState(state: GameState, playerId: string) {
 | 
			
		||||
    this.state = state
 | 
			
		||||
    const { lastMove } = state
 | 
			
		||||
 | 
			
		||||
    if (lastMove === null) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    console.log('lastMove :>> ', lastMove)
 | 
			
		||||
    if (
 | 
			
		||||
      lastMove !== null &&
 | 
			
		||||
      lastMove.tile !== undefined &&
 | 
			
		||||
      lastMove.tile.pips !== undefined &&
 | 
			
		||||
      lastMove.playerId !== playerId
 | 
			
		||||
    ) {
 | 
			
		||||
      const tile = new Tile(lastMove.tile.id, this.ticker, lastMove.tile.pips, this.scale)
 | 
			
		||||
      this.nextTile = tile
 | 
			
		||||
      lastMove.tile = tile.toPlain()
 | 
			
		||||
      this.movements.push(lastMove)
 | 
			
		||||
      this.addTile(tile, lastMove)
 | 
			
		||||
      this.setFreeEnd(lastMove)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getOrientationII(tile: TileDto, side: string) {
 | 
			
		||||
    let orientation = ''
 | 
			
		||||
    const isPair = isTilePair(tile)
 | 
			
		||||
    if (side === 'left') {
 | 
			
		||||
      if (this.freeEnds !== undefined && tile.pips !== undefined) {
 | 
			
		||||
        const isInverted = this.freeEnds[0] === tile.pips[1]
 | 
			
		||||
        if (this.leftDirection === 'east') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'east' : 'west'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'north'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.leftDirection === 'west') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'west' : 'east'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'north'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.leftDirection === 'north') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'north' : 'south'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'west'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.leftDirection === 'south') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'south' : 'north'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'west'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (side === 'right') {
 | 
			
		||||
      if (this.freeEnds !== undefined && tile.pips !== undefined) {
 | 
			
		||||
        const isInverted = this.freeEnds[1] === tile.pips[0]
 | 
			
		||||
        if (this.rightDirection === 'east') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'west' : 'east'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'north'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.rightDirection === 'west') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'east' : 'west'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'north'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.rightDirection === 'north') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'south' : 'north'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'west'
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.rightDirection === 'south') {
 | 
			
		||||
          if (!isPair) {
 | 
			
		||||
            orientation = isInverted ? 'north' : 'south'
 | 
			
		||||
          } else {
 | 
			
		||||
            orientation = 'west'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return orientation
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addTile(tile: Tile, move: Movement) {
 | 
			
		||||
    let orientation = ''
 | 
			
		||||
    let x: number =
 | 
			
		||||
      move.type === 'left'
 | 
			
		||||
        ? this.scaleX.inverse(this.leftTile?.x ?? 0)
 | 
			
		||||
        : this.scaleX.inverse(this.rightTile?.x ?? 0)
 | 
			
		||||
    let y: number =
 | 
			
		||||
      move.type === 'left'
 | 
			
		||||
        ? this.scaleY.inverse(this.leftTile?.y ?? 0)
 | 
			
		||||
        : this.scaleY.inverse(this.rightTile?.y ?? 0)
 | 
			
		||||
    const isLeft = move.type === 'left'
 | 
			
		||||
    const tileDto = tile.toPlain()
 | 
			
		||||
    let direction = move.type === 'left' ? this.leftDirection : this.rightDirection
 | 
			
		||||
 | 
			
		||||
    if (this.tiles.length === 0) {
 | 
			
		||||
      x = 0
 | 
			
		||||
      y = 0
 | 
			
		||||
      if (tile.isPair()) {
 | 
			
		||||
        orientation = 'north'
 | 
			
		||||
      } else {
 | 
			
		||||
        orientation = 'east'
 | 
			
		||||
      }
 | 
			
		||||
      this.firstTile = tile
 | 
			
		||||
    } else if (move.direction !== undefined) {
 | 
			
		||||
      direction = move.direction
 | 
			
		||||
      const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
			
		||||
      const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
			
		||||
      const directionIndex = DIRECTIONS.indexOf(direction)
 | 
			
		||||
      isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
			
		||||
      const availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
			
		||||
      orientation = this.getOrientationII(tileDto, move.type)
 | 
			
		||||
      availablePosition && ([x, y] = availablePosition)
 | 
			
		||||
    } else {
 | 
			
		||||
      if (this.tiles.length === 0) {
 | 
			
		||||
        x = 0
 | 
			
		||||
        y = 0
 | 
			
		||||
        if (tile.isPair()) {
 | 
			
		||||
          tile.setOrientation('north')
 | 
			
		||||
        } else {
 | 
			
		||||
          tile.setOrientation('east')
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
			
		||||
        const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
			
		||||
 | 
			
		||||
        let directionIndex = DIRECTIONS.indexOf(direction)
 | 
			
		||||
        let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
			
		||||
        let endlessLoop: number = 0
 | 
			
		||||
        while (endlessLoop < 4 && availablePosition === undefined) {
 | 
			
		||||
          directionIndex = (directionIndex + 1) % 4
 | 
			
		||||
          availablePosition = availablePositions[directionIndex]
 | 
			
		||||
          endlessLoop++
 | 
			
		||||
        }
 | 
			
		||||
        if (endlessLoop >= 4) {
 | 
			
		||||
          throw new Error('No available position')
 | 
			
		||||
        }
 | 
			
		||||
        direction = DIRECTIONS[directionIndex]
 | 
			
		||||
        isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
			
		||||
        orientation = this.getOrientationII(tileDto, move.type)
 | 
			
		||||
        availablePosition && ([x, y] = availablePosition)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const endTile = isLeft ? this.leftTile : this.rightTile
 | 
			
		||||
    const isEndVertical = endTile?.isVertical() ?? false
 | 
			
		||||
    const isNextVertical = orientation === 'north' || orientation === 'south'
 | 
			
		||||
    if (this.tiles.length > 0 && endTile !== undefined) {
 | 
			
		||||
      //!tile.equals(endTile)
 | 
			
		||||
      if (direction === 'east') {
 | 
			
		||||
        x += !isEndVertical && isNextVertical ? 0 : 1
 | 
			
		||||
      } else if (direction === 'west') {
 | 
			
		||||
        x -= !isEndVertical && isNextVertical ? 0 : 1
 | 
			
		||||
      } else if (direction === 'north') {
 | 
			
		||||
        y -= isEndVertical && !isNextVertical ? 0 : 1
 | 
			
		||||
      } else if (direction === 'south') {
 | 
			
		||||
        y += isEndVertical && !isNextVertical ? 0 : 1
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    console.log('position::>>', tile.pips, x, y)
 | 
			
		||||
    tile.setPosition(this.scaleX(x), this.scaleY(y))
 | 
			
		||||
    tile.setOrientation(orientation)
 | 
			
		||||
    tile.reScale(this.scale)
 | 
			
		||||
    this.tiles.push(tile)
 | 
			
		||||
    tile.addTo(this.tilesContainer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPlayedTile(id: string): Tile | undefined {
 | 
			
		||||
    return this.tiles.find((t) => t.id === id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTileInHand(id: string): Tile | undefined {
 | 
			
		||||
    return this.playerHand.find((t) => t.id === id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setFreeEnd(move: Movement) {
 | 
			
		||||
    const { type, tile: tileDto } = move
 | 
			
		||||
    const tile = this.getPlayedTile(tileDto?.id ?? '')
 | 
			
		||||
 | 
			
		||||
    if (tile === undefined) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { pips } = tile
 | 
			
		||||
 | 
			
		||||
    if (pips === undefined) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.freeEnds === undefined) {
 | 
			
		||||
      this.leftTile = this.rightTile = tile
 | 
			
		||||
      if (tile.isPair()) {
 | 
			
		||||
        this.freeEnds = [pips[0], pips[1]]
 | 
			
		||||
      } else {
 | 
			
		||||
        this.freeEnds = [pips[1], pips[0]]
 | 
			
		||||
      }
 | 
			
		||||
    } else if (type === 'left') {
 | 
			
		||||
      this.leftTile = tile
 | 
			
		||||
      if (tile.isPair()) {
 | 
			
		||||
        this.freeEnds[0] = pips[0]
 | 
			
		||||
      } else {
 | 
			
		||||
        this.freeEnds[0] = pips[0] === this.freeEnds[0] ? pips[1] : pips[0]
 | 
			
		||||
      }
 | 
			
		||||
    } else if (type === 'right') {
 | 
			
		||||
      this.rightTile = tile
 | 
			
		||||
      if (tile.isPair()) {
 | 
			
		||||
        this.freeEnds[1] = pips[0]
 | 
			
		||||
      } else {
 | 
			
		||||
        this.freeEnds[1] = pips[0] === this.freeEnds[1] ? pips[1] : pips[0]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateBoard(move: Movement) {
 | 
			
		||||
    try {
 | 
			
		||||
      const { tile: tileDto } = move
 | 
			
		||||
      const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
			
		||||
 | 
			
		||||
      if (tile === undefined) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.movements.push(move)
 | 
			
		||||
      this.addTile(tile, move)
 | 
			
		||||
      this.setFreeEnd(move)
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.log('error :>> ', error)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setMovesForTile(values: [boolean, boolean], tile: TileDto) {
 | 
			
		||||
    this.setValidEnds(values, tile)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setValidEnds(values: boolean[], tile: TileDto) {
 | 
			
		||||
    console.log('validEnds')
 | 
			
		||||
    if (this.count === 0) {
 | 
			
		||||
      this.createInteractionsII('right', [[0, 0], undefined, undefined, undefined])
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (values[0]) {
 | 
			
		||||
      const side = 'left'
 | 
			
		||||
      const validInteractions = this.nextTileValidMoves(tile, side)
 | 
			
		||||
      const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
 | 
			
		||||
      console.log('validInteractions :>> ', validInteractions)
 | 
			
		||||
      // this.createInteractions(side, tile)
 | 
			
		||||
      this.createInteractionsII(side, validPoints)
 | 
			
		||||
    }
 | 
			
		||||
    if (values[1]) {
 | 
			
		||||
      const side = 'right'
 | 
			
		||||
      const validInteractions = this.nextTileValidMoves(tile, side)
 | 
			
		||||
      const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
 | 
			
		||||
      console.log('validInteractions :>> ', validInteractions)
 | 
			
		||||
      this.createInteractionsII(side, validPoints)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  nextTileValidPoints(
 | 
			
		||||
    tile: TileDto,
 | 
			
		||||
    side: string,
 | 
			
		||||
    validMoves: boolean[]
 | 
			
		||||
  ): ([number, number] | undefined)[] {
 | 
			
		||||
    const isLeft = side === 'left'
 | 
			
		||||
    const end = isLeft ? this.leftTile : this.rightTile
 | 
			
		||||
    const isEndVertical = end?.isVertical() ?? false
 | 
			
		||||
    const direction = isLeft ? this.leftDirection : this.rightDirection
 | 
			
		||||
    const signX = direction === 'west' ? -1 : 1
 | 
			
		||||
    const signY = direction === 'south' ? 1 : -1
 | 
			
		||||
    const isEndPair = end?.isPair() ?? false
 | 
			
		||||
 | 
			
		||||
    const x = this.scaleX.inverse(end?.x ?? 0)
 | 
			
		||||
    const y = this.scaleY.inverse(end?.y ?? 0)
 | 
			
		||||
 | 
			
		||||
    let pointNorth: [number, number] | undefined = undefined
 | 
			
		||||
    let pointSouth: [number, number] | undefined = undefined
 | 
			
		||||
    let pointEast: [number, number] | undefined = undefined
 | 
			
		||||
    let pointWest: [number, number] | undefined = undefined
 | 
			
		||||
 | 
			
		||||
    if (this.count !== 0) {
 | 
			
		||||
      if (validMoves[0]) {
 | 
			
		||||
        // north
 | 
			
		||||
        if (isEndVertical) {
 | 
			
		||||
          pointNorth = [x, y - 3]
 | 
			
		||||
        } else {
 | 
			
		||||
          pointNorth = isEndPair ? [x, y - 2] : [x + 1 * signX, y - 2]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (validMoves[2]) {
 | 
			
		||||
        // south
 | 
			
		||||
        if (isEndVertical) {
 | 
			
		||||
          pointSouth = [x, y + 3]
 | 
			
		||||
        } else {
 | 
			
		||||
          pointSouth = isEndPair ? [x, y + 2] : [x + 1 * signX, y + 2]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (validMoves[1]) {
 | 
			
		||||
        // east
 | 
			
		||||
        if (isEndVertical) {
 | 
			
		||||
          pointEast = isEndPair ? [x + 2, y] : [x + 2, y + 1 * signY]
 | 
			
		||||
        } else {
 | 
			
		||||
          pointEast = [x + 3, y]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (validMoves[3]) {
 | 
			
		||||
        // west
 | 
			
		||||
        if (isEndVertical) {
 | 
			
		||||
          pointWest = isEndPair ? [x - 2, y] : [x - 2, y + 1 * signY]
 | 
			
		||||
        } else {
 | 
			
		||||
          pointWest = [x - 3, y]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [pointNorth, pointEast, pointSouth, pointWest]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createInteractionsII(side: string, validInteractions: ([number, number] | undefined)[]) {
 | 
			
		||||
    if (validInteractions[0] !== undefined) {
 | 
			
		||||
      this.addInteraction(validInteractions[0][0], validInteractions[0][1], side, 'north')
 | 
			
		||||
    }
 | 
			
		||||
    if (validInteractions[1] !== undefined) {
 | 
			
		||||
      this.addInteraction(validInteractions[1][0], validInteractions[1][1], side, 'east')
 | 
			
		||||
    }
 | 
			
		||||
    if (validInteractions[2] !== undefined) {
 | 
			
		||||
      this.addInteraction(validInteractions[2][0], validInteractions[2][1], side, 'south')
 | 
			
		||||
    }
 | 
			
		||||
    if (validInteractions[3] !== undefined) {
 | 
			
		||||
      this.addInteraction(validInteractions[3][0], validInteractions[3][1], side, 'west')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cleanInteractions() {
 | 
			
		||||
    this.interactionContainer.removeChildren()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addInteraction(x: number, y: number, side: string, direction?: string) {
 | 
			
		||||
    const dot = new Dot(this.ticker, this.scale)
 | 
			
		||||
    dot.alpha = 0.5
 | 
			
		||||
    dot.interactive = true
 | 
			
		||||
    dot.on('pointerdown', () => {
 | 
			
		||||
      console.log('direction :>> ', direction)
 | 
			
		||||
      this.emit(`${side}Click`, direction && { direction, x, y })
 | 
			
		||||
      this.cleanInteractions()
 | 
			
		||||
    })
 | 
			
		||||
    dot.on('pointerover', () => {
 | 
			
		||||
      dot.alpha = 1
 | 
			
		||||
    })
 | 
			
		||||
    dot.on('pointerout', () => {
 | 
			
		||||
      dot.alpha = 0.5
 | 
			
		||||
    })
 | 
			
		||||
    dot.setPosition(this.scaleX(x), this.scaleY(y))
 | 
			
		||||
    dot.addTo(this.interactionContainer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  nextTileValidMoves(tile: TileDto, side: string): boolean[] {
 | 
			
		||||
    if (tile === undefined || this.freeEnds === undefined) return [false, false, false, false]
 | 
			
		||||
    if (this.count === 0) return [false, true, false, true] // depends on game mode
 | 
			
		||||
 | 
			
		||||
    const isLeft = side === 'left'
 | 
			
		||||
    const end = isLeft ? this.freeEnds[0] : this.freeEnds[1]
 | 
			
		||||
    const endTile = isLeft ? this.leftTile : this.rightTile
 | 
			
		||||
    const direction = isLeft ? this.leftDirection : this.rightDirection
 | 
			
		||||
    const signX = isLeft ? -1 : 1
 | 
			
		||||
    const signY = direction === 'south' ? 1 : -1
 | 
			
		||||
    const validMove = tile.pips?.includes(end) ?? false
 | 
			
		||||
    const endX = endTile?.x ?? 0
 | 
			
		||||
    const endY = endTile?.y ?? 0
 | 
			
		||||
    const tileHeight = endTile?.height ?? 0
 | 
			
		||||
    const margin = 20
 | 
			
		||||
    const spaceNeeded = tileHeight / 2 + tileHeight + margin
 | 
			
		||||
    const isSecond = this.count === 1
 | 
			
		||||
    const isPair = isTilePair(tile)
 | 
			
		||||
 | 
			
		||||
    let canPlayNorth = false
 | 
			
		||||
    let canPlaySouth = false
 | 
			
		||||
    let canPlayEast = false
 | 
			
		||||
    let canPlayWest = false
 | 
			
		||||
 | 
			
		||||
    if (validMove) {
 | 
			
		||||
      canPlayEast = direction !== 'west' && endX + signX * spaceNeeded < this.width
 | 
			
		||||
      canPlayWest = direction !== 'east' && endX + signX * spaceNeeded > 0
 | 
			
		||||
      canPlayNorth = direction !== 'south' && endY + signY * spaceNeeded > 0
 | 
			
		||||
      canPlaySouth = direction !== 'north' && endY + signY * spaceNeeded < this.height
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isSecond) {
 | 
			
		||||
      canPlayNorth = canPlaySouth = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isPair && !endTile?.isVertical()) {
 | 
			
		||||
      canPlayNorth = canPlaySouth = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isPair && endTile?.isVertical()) {
 | 
			
		||||
      if (direction === 'north') {
 | 
			
		||||
        canPlaySouth = false
 | 
			
		||||
      } else if (direction === 'south') {
 | 
			
		||||
        canPlayNorth = false
 | 
			
		||||
      } else if (direction === 'east') {
 | 
			
		||||
        canPlayWest = false
 | 
			
		||||
      } else if (direction === 'west') {
 | 
			
		||||
        canPlayEast = false
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/game/Dot.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/game/Dot.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
import { Texture, Ticker } from 'pixi.js'
 | 
			
		||||
import { SpriteBase } from './SpriteBase'
 | 
			
		||||
 | 
			
		||||
export class Dot extends SpriteBase {
 | 
			
		||||
  constructor(ticker: Ticker, scale: number = 1) {
 | 
			
		||||
    super(ticker, scale)
 | 
			
		||||
    this.sprite.texture = this.createTexture()
 | 
			
		||||
    this.anchor = 0.5
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTexture(): Texture {
 | 
			
		||||
    return Texture.from('dot')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										136
									
								
								src/game/Game.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/game/Game.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
import { Application, Assets } from 'pixi.js'
 | 
			
		||||
import { Board } from '@/game/Board'
 | 
			
		||||
import { assets } from '@/game/utilities/assets'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import { Hand } from '@/game/Hand'
 | 
			
		||||
import type { Movement, TileDto } from '@/common/interfaces'
 | 
			
		||||
 | 
			
		||||
export class Game {
 | 
			
		||||
  public board!: Board
 | 
			
		||||
  public hand!: Hand
 | 
			
		||||
  private app: Application = new Application()
 | 
			
		||||
  private selectedTile: TileDto | undefined
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private options: { boardScale: number; handScale: number; width: number; height: number } = {
 | 
			
		||||
      boardScale: 1,
 | 
			
		||||
      handScale: 1,
 | 
			
		||||
      width: 1200,
 | 
			
		||||
      height: 650
 | 
			
		||||
    },
 | 
			
		||||
    private emit: any,
 | 
			
		||||
    private props: any
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async setup(): Promise<HTMLCanvasElement> {
 | 
			
		||||
    const width = 1200
 | 
			
		||||
    const height = 650
 | 
			
		||||
 | 
			
		||||
    await this.app.init({ width, height })
 | 
			
		||||
    return this.app.canvas
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  start() {
 | 
			
		||||
    this.board = new Board(this.app)
 | 
			
		||||
    this.hand = new Hand(this.app)
 | 
			
		||||
    this.hand.scale = this.options.handScale
 | 
			
		||||
    this.board.scale = this.options.boardScale
 | 
			
		||||
    this.setBoardEvents()
 | 
			
		||||
    this.setHandEvents()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async preload() {
 | 
			
		||||
    await Assets.load(assets)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  destroy() {
 | 
			
		||||
    this.removeBoardEvents()
 | 
			
		||||
    this.removeHandEvents()
 | 
			
		||||
    this.app.destroy()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private setHandEvents() {
 | 
			
		||||
    this.hand.on('handUpdated', (tiles: Tile[]) => {
 | 
			
		||||
      this.board.setPlayerHand(tiles)
 | 
			
		||||
    })
 | 
			
		||||
    this.hand.on('tileClick', (tile: TileDto) => {
 | 
			
		||||
      this.selectedTile = tile
 | 
			
		||||
      if (tile !== undefined) {
 | 
			
		||||
        this.board.setMovesForTile(this.getMoves(tile), tile)
 | 
			
		||||
      } else {
 | 
			
		||||
        this.board.cleanInteractions()
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.hand.on('passClick', () => {
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        id: '',
 | 
			
		||||
        type: 'pass',
 | 
			
		||||
        playerId: this.props.playerId ?? ''
 | 
			
		||||
      }
 | 
			
		||||
      this.emit('move', move)
 | 
			
		||||
      this.board.updateBoard(move)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getMoves(tile: any): [boolean, boolean] {
 | 
			
		||||
    if (tile === undefined) return [false, false]
 | 
			
		||||
    if (this.board.count === 0) return [false, true]
 | 
			
		||||
 | 
			
		||||
    const validEnds: [boolean, boolean] = [false, false]
 | 
			
		||||
    const freeEnds = this.board.freeEnds
 | 
			
		||||
 | 
			
		||||
    if (freeEnds !== undefined) {
 | 
			
		||||
      if (tile.pips != undefined) {
 | 
			
		||||
        validEnds[0] = tile.pips.includes(freeEnds[0])
 | 
			
		||||
        validEnds[1] = tile.pips.includes(freeEnds[1])
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return validEnds
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public setCanMakeMove(value: boolean) {
 | 
			
		||||
    this.hand.canMove = value
 | 
			
		||||
    this.board.canMove = value
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private setBoardEvents() {
 | 
			
		||||
    this.board.on('leftClick', (data) => {
 | 
			
		||||
      console.log('left data :>> ', data)
 | 
			
		||||
      if (this.selectedTile === undefined) return
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
        type: 'left',
 | 
			
		||||
        playerId: this.props.playerId ?? '',
 | 
			
		||||
        ...data
 | 
			
		||||
      }
 | 
			
		||||
      this.emit('move', move)
 | 
			
		||||
      this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.board.on('rightClick', (data) => {
 | 
			
		||||
      console.log('right data :>> ', data)
 | 
			
		||||
      if (this.selectedTile === undefined) return
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
        type: 'right',
 | 
			
		||||
        playerId: this.props.playerId ?? '',
 | 
			
		||||
        ...data
 | 
			
		||||
      }
 | 
			
		||||
      this.emit('move', move)
 | 
			
		||||
      this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private removeBoardEvents() {
 | 
			
		||||
    this.board.off('leftClick')
 | 
			
		||||
    this.board.off('rightClick')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private removeHandEvents() {
 | 
			
		||||
    this.hand.off('tileClick')
 | 
			
		||||
    this.hand.off('passClick')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										205
									
								
								src/game/Hand.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/game/Hand.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
			
		||||
import {
 | 
			
		||||
  Application,
 | 
			
		||||
  Container,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Graphics,
 | 
			
		||||
  Sprite,
 | 
			
		||||
  Text,
 | 
			
		||||
  Texture,
 | 
			
		||||
  Ticker
 | 
			
		||||
} from 'pixi.js'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import type { PlayerState, TileDto } from '@/common/interfaces'
 | 
			
		||||
import { GlowFilter } from 'pixi-filters'
 | 
			
		||||
 | 
			
		||||
export class Hand extends EventEmitter {
 | 
			
		||||
  tiles: Tile[] = []
 | 
			
		||||
  container: Container = new Container()
 | 
			
		||||
  buttonPassContainer: Container = new Container()
 | 
			
		||||
  height: number
 | 
			
		||||
  width: number
 | 
			
		||||
  ticker: Ticker
 | 
			
		||||
  lastTimeClicked: number = 0
 | 
			
		||||
  doubleClickThreshold: number = 300
 | 
			
		||||
  initialized: boolean = false
 | 
			
		||||
  _canMove: boolean = false
 | 
			
		||||
  scale: number = 1
 | 
			
		||||
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
    app.stage.addChild(this.container)
 | 
			
		||||
    this.ticker = app.ticker
 | 
			
		||||
    this.height = 130
 | 
			
		||||
    this.width = app.canvas.width
 | 
			
		||||
    this.container.y = app.canvas.height - this.height
 | 
			
		||||
    this.container.width = this.width
 | 
			
		||||
    this.container.height = this.height
 | 
			
		||||
    this.addBg()
 | 
			
		||||
    this.createPassButton()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get canMove() {
 | 
			
		||||
    return this._canMove
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set canMove(value: boolean) {
 | 
			
		||||
    this._canMove = value
 | 
			
		||||
    this.buttonPassContainer.eventMode = value ? 'static' : 'none'
 | 
			
		||||
    this.buttonPassContainer.cursor = value ? 'pointer' : 'default'
 | 
			
		||||
    this.tiles.forEach((tile) => {
 | 
			
		||||
      tile.interactive = value
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initialize(playerState: PlayerState) {
 | 
			
		||||
    this.tiles = this.createTiles(playerState)
 | 
			
		||||
    this.emit('handUpdated', this.tiles)
 | 
			
		||||
    this.initialized = this.tiles.length > 0
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private addBg() {
 | 
			
		||||
    const bg = new Sprite(Texture.WHITE)
 | 
			
		||||
    bg.alpha = 0.08
 | 
			
		||||
    bg.width = this.width
 | 
			
		||||
    bg.height = this.height
 | 
			
		||||
    this.container.addChild(bg)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onTileClick(tile: Tile) {
 | 
			
		||||
    // if (Date.now() - this.lastTimeClicked < this.doubleClickThreshold) {
 | 
			
		||||
    //   this.emit('tileDoubleClick', { id: tile.id })
 | 
			
		||||
    //   return
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    const selected = this.tiles.find((t) => t.selected)
 | 
			
		||||
    if (selected) {
 | 
			
		||||
      this.deselectTile(selected)
 | 
			
		||||
      if (selected.id === tile.id) {
 | 
			
		||||
        this.emit('tileClick')
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tile.selected = true
 | 
			
		||||
    tile.alpha = 1
 | 
			
		||||
 | 
			
		||||
    tile.animateTo(tile.x, tile.y - 10)
 | 
			
		||||
    this.emit('tileClick', tile.toPlain())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private deselectTile(selected: Tile) {
 | 
			
		||||
    selected.animateTo(selected.x, selected.y + 10)
 | 
			
		||||
    selected.selected = false
 | 
			
		||||
    selected.alpha = 0.7
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public tileMoved(tileDto: TileDto) {
 | 
			
		||||
    const tile = this.tiles.find((t) => t.id === tileDto.id)
 | 
			
		||||
 | 
			
		||||
    if (!tile) return
 | 
			
		||||
 | 
			
		||||
    tile.interactive = false
 | 
			
		||||
    tile.clearFilters()
 | 
			
		||||
    tile.off('pointerdown')
 | 
			
		||||
    tile.off('pointerover')
 | 
			
		||||
    tile.off('pointerout')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private createPassButton() {
 | 
			
		||||
    const rectangle = new Graphics().roundRect(0, 0, 80, 30, 10).fill(0xffff00)
 | 
			
		||||
 | 
			
		||||
    const text = new Text({
 | 
			
		||||
      text: 'PASS',
 | 
			
		||||
      style: {
 | 
			
		||||
        fontFamily: 'Arial',
 | 
			
		||||
        fontSize: 12,
 | 
			
		||||
        fontWeight: 'bold',
 | 
			
		||||
        fill: 0x121212,
 | 
			
		||||
        align: 'center'
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    text.anchor = 0.5
 | 
			
		||||
 | 
			
		||||
    this.buttonPassContainer = new Container()
 | 
			
		||||
 | 
			
		||||
    this.buttonPassContainer.addChild(rectangle)
 | 
			
		||||
    this.buttonPassContainer.addChild(text)
 | 
			
		||||
 | 
			
		||||
    text.y = this.buttonPassContainer.height / 2 - 4
 | 
			
		||||
    text.x = this.buttonPassContainer.width / 2 - 8
 | 
			
		||||
 | 
			
		||||
    this.buttonPassContainer.eventMode = 'none'
 | 
			
		||||
    this.buttonPassContainer.cursor = 'default'
 | 
			
		||||
    this.buttonPassContainer.x = 20
 | 
			
		||||
    this.buttonPassContainer.y = this.height / 2 - 10
 | 
			
		||||
    rectangle.alpha = 0.7
 | 
			
		||||
    text.alpha = 0.7
 | 
			
		||||
    this.buttonPassContainer.on('pointerdown', () => {
 | 
			
		||||
      this.emit('passClick')
 | 
			
		||||
    })
 | 
			
		||||
    this.buttonPassContainer.on('pointerover', () => {
 | 
			
		||||
      rectangle.alpha = 1
 | 
			
		||||
      text.alpha = 1
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.buttonPassContainer.on('pointerout', () => {
 | 
			
		||||
      rectangle.alpha = 0.7
 | 
			
		||||
      text.alpha = 0.7
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.container.addChild(this.buttonPassContainer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update(playerState: PlayerState) {
 | 
			
		||||
    if (!this.initialized) {
 | 
			
		||||
      this.initialize(playerState)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const missing: Tile | undefined = this.tiles.find(
 | 
			
		||||
      (tile: Tile) => !playerState.hand.find((t) => t.id === tile.id)
 | 
			
		||||
    )
 | 
			
		||||
    if (missing) {
 | 
			
		||||
      this.container.removeChild(missing.getSprite())
 | 
			
		||||
      this.tiles = this.tiles.filter((tile) => tile.id !== missing.id)
 | 
			
		||||
      this.emit('handUpdated', this.tiles)
 | 
			
		||||
    }
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private createTiles(playerState: PlayerState) {
 | 
			
		||||
    return playerState.hand.map((tile) => {
 | 
			
		||||
      const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
 | 
			
		||||
      newTile.alpha = 0.7
 | 
			
		||||
      newTile.anchor = 0.5
 | 
			
		||||
      newTile.addTo(this.container)
 | 
			
		||||
      newTile.on('pointerdown', () => this.onTileClick(newTile))
 | 
			
		||||
      newTile.on('pointerover', () => {
 | 
			
		||||
        newTile.alpha = 1
 | 
			
		||||
        newTile.setFilters([
 | 
			
		||||
          new GlowFilter({
 | 
			
		||||
            distance: 10,
 | 
			
		||||
            outerStrength: 2,
 | 
			
		||||
            innerStrength: 1,
 | 
			
		||||
            color: 0xffffff,
 | 
			
		||||
            quality: 0.5
 | 
			
		||||
          })
 | 
			
		||||
        ])
 | 
			
		||||
      })
 | 
			
		||||
      newTile.on('pointerout', () => {
 | 
			
		||||
        if (!newTile.selected) {
 | 
			
		||||
          newTile.alpha = 0.7
 | 
			
		||||
          newTile.getSprite().filters = []
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      return newTile
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderTiles() {
 | 
			
		||||
    const deltaX = this.width / 2 - (this.tiles.length * 50 - 5) / 2
 | 
			
		||||
    this.tiles.forEach((tile, i) => {
 | 
			
		||||
      tile.setPosition(deltaX + tile.width / 2 + i * (tile.width + 5), tile.height / 2 + 20)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								src/game/SpriteBase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/game/SpriteBase.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
import { Sprite, Texture, Ticker } from 'pixi.js'
 | 
			
		||||
 | 
			
		||||
export abstract class SpriteBase {
 | 
			
		||||
  private _interactive: boolean = false
 | 
			
		||||
  protected sprite: Sprite = new Sprite()
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    protected ticker?: Ticker,
 | 
			
		||||
    protected scale: number = 1
 | 
			
		||||
  ) {
 | 
			
		||||
    this.ticker = ticker
 | 
			
		||||
    this.scale = scale
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  abstract createTexture(): Texture
 | 
			
		||||
 | 
			
		||||
  set interactive(value: boolean) {
 | 
			
		||||
    this._interactive = value
 | 
			
		||||
    this.sprite.eventMode = value ? 'static' : 'none'
 | 
			
		||||
    this.sprite.cursor = value ? 'pointer' : 'default'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get interactive(): boolean {
 | 
			
		||||
    return this._interactive
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get x(): number {
 | 
			
		||||
    return this.sprite.x
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get y(): number {
 | 
			
		||||
    return this.sprite.y
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get width(): number {
 | 
			
		||||
    return this.sprite.width
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get height(): number {
 | 
			
		||||
    return this.sprite.height
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set alpha(value: number) {
 | 
			
		||||
    this.sprite.alpha = value
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get alpha(): number {
 | 
			
		||||
    return this.sprite.alpha
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set anchor(value: { x: number; y: number } | number) {
 | 
			
		||||
    if (typeof value === 'number') {
 | 
			
		||||
      this.sprite.anchor.set(value)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    this.sprite.anchor.set(value.x, value.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  on(event: string, fn: any): void {
 | 
			
		||||
    this.sprite.on(event, fn)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  off(event: string): void {
 | 
			
		||||
    this.sprite.off(event)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSprite(): Sprite {
 | 
			
		||||
    return this.sprite
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPosition(x: number, y: number) {
 | 
			
		||||
    this.sprite.x = x
 | 
			
		||||
    this.sprite.y = y
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setFilters(filters: any[]) {
 | 
			
		||||
    this.sprite.filters = filters
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearFilters() {
 | 
			
		||||
    this.sprite.filters = []
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  animateTo(x: number, y: number) {
 | 
			
		||||
    const initialX = this.sprite.x
 | 
			
		||||
    const initialY = this.sprite.y
 | 
			
		||||
 | 
			
		||||
    const deltaX = x - this.sprite.x
 | 
			
		||||
    const deltaY = y - this.sprite.y
 | 
			
		||||
    let elapsed: number = 0
 | 
			
		||||
    const duration: number = 10
 | 
			
		||||
 | 
			
		||||
    const tick: any = (delta: any) => {
 | 
			
		||||
      elapsed += delta.deltaTime
 | 
			
		||||
      const progress = Math.min(elapsed / duration, 1)
 | 
			
		||||
 | 
			
		||||
      this.sprite.x = initialX + deltaX * progress
 | 
			
		||||
      this.sprite.y = initialY + deltaY * progress
 | 
			
		||||
 | 
			
		||||
      if (progress === 1) {
 | 
			
		||||
        this.ticker?.remove(tick)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.ticker?.add(tick)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addTo(container: any) {
 | 
			
		||||
    container.addChild(this.sprite)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								src/game/Tile.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/game/Tile.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
import { Texture, Ticker } from 'pixi.js'
 | 
			
		||||
import { SpriteBase } from '@/game/SpriteBase'
 | 
			
		||||
 | 
			
		||||
export class Tile extends SpriteBase {
 | 
			
		||||
  selected: boolean = false
 | 
			
		||||
  orientation: string
 | 
			
		||||
  // sprite: Sprite = new Sprite()
 | 
			
		||||
  // private _interactive: boolean = false
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public id: string,
 | 
			
		||||
    ticker?: Ticker,
 | 
			
		||||
    public pips?: [number, number],
 | 
			
		||||
    scale: number = 1
 | 
			
		||||
  ) {
 | 
			
		||||
    super(ticker, scale)
 | 
			
		||||
    this.id = id
 | 
			
		||||
    // this.ticker = ticker
 | 
			
		||||
    this.sprite.texture = this.createTexture()
 | 
			
		||||
    this.sprite.anchor.set(0.5)
 | 
			
		||||
    this.sprite.height = 100 * this.scale
 | 
			
		||||
    this.sprite.width = 50 * this.scale
 | 
			
		||||
    this.pips = pips
 | 
			
		||||
    this.orientation = 'north'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toPlain() {
 | 
			
		||||
    return {
 | 
			
		||||
      id: this.id,
 | 
			
		||||
      pips: this.pips,
 | 
			
		||||
      orientation: this.orientation,
 | 
			
		||||
      x: this.sprite.x,
 | 
			
		||||
      y: this.sprite.y,
 | 
			
		||||
      width: this.sprite.width,
 | 
			
		||||
      height: this.sprite.height
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  equals(tile: Tile | undefined): boolean {
 | 
			
		||||
    if (this.pips === undefined || tile === undefined || tile.pips === undefined) return false
 | 
			
		||||
    return (
 | 
			
		||||
      (this.pips[0] === tile.pips[0] && this.pips[1] === tile.pips[1]) ||
 | 
			
		||||
      (this.pips[0] === tile.pips[1] && this.pips[1] === tile.pips[0])
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTexture(): Texture {
 | 
			
		||||
    if (this.pips !== undefined) {
 | 
			
		||||
      const sortedInversrPips = this.pips.slice().sort((a, b) => b - a)
 | 
			
		||||
      return Texture.from(`tile-${sortedInversrPips[0]}_${sortedInversrPips[1]}`)
 | 
			
		||||
    } else {
 | 
			
		||||
      return Texture.from('tile-back')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reScale(scale: number) {
 | 
			
		||||
    this.scale = scale
 | 
			
		||||
    this.sprite.height = 100 * this.scale
 | 
			
		||||
    this.sprite.width = 50 * this.scale
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isPair(): boolean {
 | 
			
		||||
    return this.pips !== undefined && this.pips[0] === this.pips[1]
 | 
			
		||||
  }
 | 
			
		||||
  isVertical(): boolean {
 | 
			
		||||
    return this.orientation === 'north' || this.orientation === 'south'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setOrientation(value: string) {
 | 
			
		||||
    switch (value) {
 | 
			
		||||
      case 'north':
 | 
			
		||||
        this.sprite.rotation = 0
 | 
			
		||||
        break
 | 
			
		||||
      case 'east':
 | 
			
		||||
        this.sprite.rotation = Math.PI / 2
 | 
			
		||||
        break
 | 
			
		||||
      case 'south':
 | 
			
		||||
        this.sprite.rotation = Math.PI
 | 
			
		||||
        break
 | 
			
		||||
      case 'west':
 | 
			
		||||
        this.sprite.rotation = (3 * Math.PI) / 2
 | 
			
		||||
        break
 | 
			
		||||
    }
 | 
			
		||||
    this.orientation = value
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								src/game/utilities/assets.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/game/utilities/assets.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
import tileBack from '@/assets/images/tiles/back.png'
 | 
			
		||||
import tile0_0 from '@/assets/images/tiles/0-0.png'
 | 
			
		||||
import tile1_0 from '@/assets/images/tiles/1-0.png'
 | 
			
		||||
import tile1_1 from '@/assets/images/tiles/1-1.png'
 | 
			
		||||
import tile2_0 from '@/assets/images/tiles/2-0.png'
 | 
			
		||||
import tile2_1 from '@/assets/images/tiles/2-1.png'
 | 
			
		||||
import tile2_2 from '@/assets/images/tiles/2-2.png'
 | 
			
		||||
import tile3_0 from '@/assets/images/tiles/3-0.png'
 | 
			
		||||
import tile3_1 from '@/assets/images/tiles/3-1.png'
 | 
			
		||||
import tile3_2 from '@/assets/images/tiles/3-2.png'
 | 
			
		||||
import tile3_3 from '@/assets/images/tiles/3-3.png'
 | 
			
		||||
import tile4_0 from '@/assets/images/tiles/4-0.png'
 | 
			
		||||
import tile4_1 from '@/assets/images/tiles/4-1.png'
 | 
			
		||||
import tile4_2 from '@/assets/images/tiles/4-2.png'
 | 
			
		||||
import tile4_3 from '@/assets/images/tiles/4-3.png'
 | 
			
		||||
import tile4_4 from '@/assets/images/tiles/4-4.png'
 | 
			
		||||
import tile5_0 from '@/assets/images/tiles/5-0.png'
 | 
			
		||||
import tile5_1 from '@/assets/images/tiles/5-1.png'
 | 
			
		||||
import tile5_2 from '@/assets/images/tiles/5-2.png'
 | 
			
		||||
import tile5_3 from '@/assets/images/tiles/5-3.png'
 | 
			
		||||
import tile5_4 from '@/assets/images/tiles/5-4.png'
 | 
			
		||||
import tile5_5 from '@/assets/images/tiles/5-5.png'
 | 
			
		||||
import tile6_0 from '@/assets/images/tiles/6-0.png'
 | 
			
		||||
import tile6_1 from '@/assets/images/tiles/6-1.png'
 | 
			
		||||
import tile6_2 from '@/assets/images/tiles/6-2.png'
 | 
			
		||||
import tile6_3 from '@/assets/images/tiles/6-3.png'
 | 
			
		||||
import tile6_4 from '@/assets/images/tiles/6-4.png'
 | 
			
		||||
import tile6_5 from '@/assets/images/tiles/6-5.png'
 | 
			
		||||
import tile6_6 from '@/assets/images/tiles/6-6.png'
 | 
			
		||||
import dot from '@/assets/images/circle.png'
 | 
			
		||||
import bgWood_1 from '@/assets/images/backgrounds/wood-1.jpg'
 | 
			
		||||
import bg_1 from '@/assets/images/backgrounds/bg-1.png'
 | 
			
		||||
 | 
			
		||||
export const assets = [
 | 
			
		||||
  { alias: 'tile-back', src: tileBack },
 | 
			
		||||
  { alias: 'tile-0_0', src: tile0_0 },
 | 
			
		||||
  { alias: 'tile-1_0', src: tile1_0 },
 | 
			
		||||
  { alias: 'tile-1_1', src: tile1_1 },
 | 
			
		||||
  { alias: 'tile-2_0', src: tile2_0 },
 | 
			
		||||
  { alias: 'tile-2_1', src: tile2_1 },
 | 
			
		||||
  { alias: 'tile-2_2', src: tile2_2 },
 | 
			
		||||
  { alias: 'tile-3_0', src: tile3_0 },
 | 
			
		||||
  { alias: 'tile-3_1', src: tile3_1 },
 | 
			
		||||
  { alias: 'tile-3_2', src: tile3_2 },
 | 
			
		||||
  { alias: 'tile-3_3', src: tile3_3 },
 | 
			
		||||
  { alias: 'tile-4_0', src: tile4_0 },
 | 
			
		||||
  { alias: 'tile-4_1', src: tile4_1 },
 | 
			
		||||
  { alias: 'tile-4_2', src: tile4_2 },
 | 
			
		||||
  { alias: 'tile-4_3', src: tile4_3 },
 | 
			
		||||
  { alias: 'tile-4_4', src: tile4_4 },
 | 
			
		||||
  { alias: 'tile-5_0', src: tile5_0 },
 | 
			
		||||
  { alias: 'tile-5_1', src: tile5_1 },
 | 
			
		||||
  { alias: 'tile-5_2', src: tile5_2 },
 | 
			
		||||
  { alias: 'tile-5_3', src: tile5_3 },
 | 
			
		||||
  { alias: 'tile-5_4', src: tile5_4 },
 | 
			
		||||
  { alias: 'tile-5_5', src: tile5_5 },
 | 
			
		||||
  { alias: 'tile-6_0', src: tile6_0 },
 | 
			
		||||
  { alias: 'tile-6_1', src: tile6_1 },
 | 
			
		||||
  { alias: 'tile-6_2', src: tile6_2 },
 | 
			
		||||
  { alias: 'tile-6_3', src: tile6_3 },
 | 
			
		||||
  { alias: 'tile-6_4', src: tile6_4 },
 | 
			
		||||
  { alias: 'tile-6_5', src: tile6_5 },
 | 
			
		||||
  { alias: 'tile-6_6', src: tile6_6 },
 | 
			
		||||
  { alias: 'dot', src: dot },
 | 
			
		||||
  { alias: 'bg-wood-1', src: bgWood_1 },
 | 
			
		||||
  { alias: 'bg-1', src: bg_1 }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										25
									
								
								src/game/utilities/fonts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/game/utilities/fonts.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import { Text, TextStyle } from 'pixi.js'
 | 
			
		||||
 | 
			
		||||
export const dropShadowStyle = {
 | 
			
		||||
  alpha: 0.5,
 | 
			
		||||
  angle: 0.3,
 | 
			
		||||
  blur: 5,
 | 
			
		||||
  distance: 4
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const mainText = new TextStyle({
 | 
			
		||||
  dropShadow: dropShadowStyle,
 | 
			
		||||
  fill: '#b71a1a',
 | 
			
		||||
  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
			
		||||
  fontWeight: 'bold',
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#658f56'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export function createText(str: string, x: number, y: number, style: TextStyle = mainText) {
 | 
			
		||||
  const text = new Text({ text: str, style })
 | 
			
		||||
  text.anchor.set(0.5, 0.5)
 | 
			
		||||
  text.x = x
 | 
			
		||||
  text.y = y
 | 
			
		||||
  return text
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										155
									
								
								src/game/utilities/mocks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/game/utilities/mocks.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,155 @@
 | 
			
		||||
import { Tile } from '../Tile'
 | 
			
		||||
import type { GameState, Movement } from '../../common/interfaces'
 | 
			
		||||
import type { Game } from '../Game'
 | 
			
		||||
import { wait } from '../../common/helpers'
 | 
			
		||||
 | 
			
		||||
export const playerState = {
 | 
			
		||||
  id: '6fddcf4f-eaa9-4c87-a599-2af944477091',
 | 
			
		||||
  name: 'arhuako',
 | 
			
		||||
  score: 0,
 | 
			
		||||
  hand: [
 | 
			
		||||
    {
 | 
			
		||||
      id: '20ce403a-dccc-472e-94b2-90ba8feac538',
 | 
			
		||||
      pips: [4, 5],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: 'a0cd790b-34bb-40fe-9b25-8dc590729e15',
 | 
			
		||||
      pips: [1, 2],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: 'f516ef48-99f2-4add-96eb-877f03bf0cb1',
 | 
			
		||||
      pips: [6, 6],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: 'dd91e12d-4be1-40e2-b1b1-128a0bc3dad7',
 | 
			
		||||
      pips: [0, 1],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: 'c472f1e8-14e4-4a5b-bef0-df0e78f01c39',
 | 
			
		||||
      pips: [1, 5],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: 'c28c55e1-820f-43b1-9366-d4b20d7f62a6',
 | 
			
		||||
      pips: [1, 3],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: '6d812874-8610-43e3-8331-6b25584e759e',
 | 
			
		||||
      pips: [0, 5],
 | 
			
		||||
      flipped: false
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const gameState_0: GameState = {
 | 
			
		||||
  id: 'f043051e-6850-444f-857c-b889220fc187',
 | 
			
		||||
  lastMove: {
 | 
			
		||||
    tile: {
 | 
			
		||||
      id: 'c3f6b525-2e6f-455a-b355-9b8c8f5075f0',
 | 
			
		||||
      pips: [6, 6]
 | 
			
		||||
    },
 | 
			
		||||
    type: 'both',
 | 
			
		||||
    playerId: '137b68a6-f26c-43f5-badf-e5ae406fd059',
 | 
			
		||||
    id: 'c71943a5-1fef-4418-bd66-39767ac36817'
 | 
			
		||||
  },
 | 
			
		||||
  gameInProgress: true,
 | 
			
		||||
  winner: null,
 | 
			
		||||
  tileSelectionPhase: false,
 | 
			
		||||
  gameBlocked: false,
 | 
			
		||||
  gameTied: false,
 | 
			
		||||
  gameId: 'd76c4f98-1f26-4390-9e8d-2d727876b710',
 | 
			
		||||
  tilesInBoneyard: [],
 | 
			
		||||
  players: [
 | 
			
		||||
    {
 | 
			
		||||
      id: 'f639f263-e415-428c-bdd2-113f00f3c392',
 | 
			
		||||
      name: 'arhuako',
 | 
			
		||||
      score: 0,
 | 
			
		||||
      hand: [
 | 
			
		||||
        '51399f92-7555-4ffc-91ed-f6b5b9041a54',
 | 
			
		||||
        'e9c40c13-c821-4bad-810c-12e7cb79b47d',
 | 
			
		||||
        '95e8412b-1938-48d5-9647-cb8617659db0',
 | 
			
		||||
        '54948d0a-b7a4-416c-a3f7-6602cfff1851',
 | 
			
		||||
        'a40e6a76-c2e1-4692-82b3-603a4412a6a9',
 | 
			
		||||
        'cb7827df-fe19-4c28-a786-a47e07c2d368',
 | 
			
		||||
        '6d5a31d3-b734-4312-b8fd-1b5efa3fc258'
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: '77b2c512-d9e5-45f3-850f-99baab1befdf',
 | 
			
		||||
      name: 'Alice (AI)',
 | 
			
		||||
      score: 0,
 | 
			
		||||
      hand: [
 | 
			
		||||
        '066fd011-cabe-483b-a985-c649f4908356',
 | 
			
		||||
        '01434825-eb3c-4baf-8ad1-cc055f982e75',
 | 
			
		||||
        'c085c90f-f69c-4f83-acb6-87c576927366',
 | 
			
		||||
        '1b5d5b46-a41c-4e69-9a02-efff9adbc201',
 | 
			
		||||
        '74833dcc-1526-4c6c-a64c-a7807e647e77',
 | 
			
		||||
        'fb7e6976-67a9-496f-9758-0b172aa069dd',
 | 
			
		||||
        '58b612f0-cf40-4b5f-ba05-8a089f19b43a'
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: '137b68a6-f26c-43f5-badf-e5ae406fd059',
 | 
			
		||||
      name: 'Bob (AI)',
 | 
			
		||||
      score: 0,
 | 
			
		||||
      hand: [
 | 
			
		||||
        '9d5dcb97-7478-4030-a0f6-4e7080a5b8e0',
 | 
			
		||||
        'a07e1aaf-e651-4678-a61c-414f4324d8e8',
 | 
			
		||||
        '4db7aebb-ab4b-4709-b208-b39176e8d70a',
 | 
			
		||||
        'd1733f9c-86a6-4597-b1ff-ec44e9a375d1',
 | 
			
		||||
        'cf6e2c1c-57f8-4342-8ff4-8046971f99db',
 | 
			
		||||
        '33cec060-7f40-4af9-9182-f98cbacbae10'
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      id: '188c1d69-8c5c-4fec-a08b-dc5fa09d49fd',
 | 
			
		||||
      name: 'Charlie (AI)',
 | 
			
		||||
      score: 0,
 | 
			
		||||
      hand: [
 | 
			
		||||
        '58433be6-e8d9-4ed2-bbdc-4328425a3687',
 | 
			
		||||
        '3e24a6cd-02ea-436a-a3e4-41ae62c717d0',
 | 
			
		||||
        '2a2723d1-6c46-4718-a1c2-fa8d2c148ac8',
 | 
			
		||||
        'd521de5e-1db1-4a2e-971d-f91cc761342c',
 | 
			
		||||
        '13d943d7-b694-4419-82f5-729117878d01',
 | 
			
		||||
        'afcf2509-acfb-47ed-ade7-d08f1a20afd2',
 | 
			
		||||
        'f50f3589-41ab-4d99-919e-c4c7a71ef5b0'
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  currentPlayer: {
 | 
			
		||||
    id: '188c1d69-8c5c-4fec-a08b-dc5fa09d49fd',
 | 
			
		||||
    name: 'arhuako'
 | 
			
		||||
  },
 | 
			
		||||
  tilesInBoard: [
 | 
			
		||||
    {
 | 
			
		||||
      id: 'c3f6b525-2e6f-455a-b355-9b8c8f5075f0',
 | 
			
		||||
      pips: [6, 6]
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  boardFreeEnds: [6, 6]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getMovement(
 | 
			
		||||
  pips: [number, number] = [6, 6],
 | 
			
		||||
  side: string = 'left',
 | 
			
		||||
  scale: number = 1
 | 
			
		||||
): Movement {
 | 
			
		||||
  const tile = new Tile('string', undefined, pips, scale)
 | 
			
		||||
  tile.orientation = 'north'
 | 
			
		||||
  return {
 | 
			
		||||
    id: 'movemnetid',
 | 
			
		||||
    type: side,
 | 
			
		||||
    tile,
 | 
			
		||||
    playerId: 'string'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function mockMove(game: Game, pips: [number, number], side: string = 'left') {
 | 
			
		||||
  await wait(1000)
 | 
			
		||||
  game.board.updateBoard(getMovement(pips, side, game.board.scale))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/game/utilities/scale.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/game/utilities/scale.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
export type ScaleFunction = Function & {
 | 
			
		||||
  inverse: Function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @param domain example: [0, 100]
 | 
			
		||||
 * @param range examaple: [0, width]
 | 
			
		||||
 */
 | 
			
		||||
export function Scale(domain: number[], range: number[]): ScaleFunction {
 | 
			
		||||
  function Fn(value: number): number {
 | 
			
		||||
    return ((value - domain[0]) * (range[1] - range[0])) / (domain[1] - domain[0]) + range[0]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Fn.inverse = function (value: number): number {
 | 
			
		||||
    return ((value - range[0]) * (domain[1] - domain[0])) / (range[1] - range[0]) + domain[0]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Fn
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user