jcbantuelle/dominion-meteor

View on GitHub
app/game/server/services/game_ender.js

Summary

Maintainability
A
1 hr
Test Coverage
GameEnder = class GameEnder {

  constructor(game) {
    this.game = game
    this.players_cards = PlayerCardsModel.find(this.game._id)
  }

  end_game() {
    this.update_game()
    this.update_players()
    this.log_game()
    this.update_player_rankings()
  }

  update_game() {
    this.game.scores = this.calculate_scores()
    this.game.finished = true
    this.game.winners = this.winners()
    GameModel.update(this.game._id, this.game)
  }

  update_players() {
    let player_ids = _.map(this.game.players, '_id')
    Meteor.users.update({_id: {$in: player_ids}}, {$unset: {current_game: ''}}, {multi: true})
  }

  log_game() {
    GameHistory.insert(_.merge(this.game, {created_at: new Date()}))
  }

  update_player_rankings() {
    if (_.size(this.game.players) === 2) {
      let usernames = _.map(this.game.players, 'username')
      let player_rankings = PlayerRankings.find({username: {$in: usernames}}).fetch()
      _.each(player_rankings, (player_ranking) => {
        let winner = _.includes(this.game.winners, player_ranking.username)
        if (winner) {
          player_ranking.wins += 1
        } else {
          player_ranking.losses += 1
        }
        let opponent_names = _.difference(usernames, [player_ranking.username])
        _.each(opponent_names, function(opponent_name) {
          let opponent_index = _.findIndex(player_ranking.opponents, function(opponent) {
            return opponent.username === opponent_name
          })
          if (opponent_index === -1) {
            player_ranking.opponents.push({
              username: opponent_name,
              wins: winner ? 1 : 0,
              losses: winner ? 0 : 1
            })
          } else {
            let opponent_ranking = player_ranking.opponents[opponent_index]
            if (winner) {
              opponent_ranking.wins += 1
            } else {
              opponent_ranking.losses += 1
            }
            player_ranking.opponents[opponent_index] = opponent_ranking
          }
        })
        PlayerRankings.update(player_ranking._id, player_ranking)
      })
    }
  }

  calculate_scores() {
    return _.chain(this.players_cards).map((player_cards) => {
      let all_cards = AllPlayerCardsQuery.find(player_cards, true).concat(player_cards.states).concat(player_cards.artifacts)
      let point_cards = this.point_cards(all_cards)
      let landmark_cards = this.landmark_cards(all_cards)
      let deck_breakdown = this.deck_breakdown(all_cards)
      let player_score = {
        username: player_cards.username,
        point_cards: point_cards,
        points: this.card_score(point_cards.concat(landmark_cards)) + player_cards.victory_tokens,
        turns: player_cards.turns,
        deck_breakdown: deck_breakdown
      }
      if (player_cards.victory_tokens > 0) {
        player_score.victory_tokens = player_cards.victory_tokens
      }
      if (_.size(landmark_cards) > 0) {
        player_score.landmark_cards = landmark_cards
      }
      return player_score
    }).sortBy(function(score) {
      return -score.points
    }).value()
  }

  point_cards(player_cards) {
    return _.chain(player_cards).map(function(player_card) {
      let card = ClassCreator.create(player_card.name)
      return {
        name: card.name(),
        types: card.type_class(),
        points: card.victory_points(player_cards, player_card.source),
        point_variable: card.point_variable(player_cards)
      }
    }).filter(function(point_card) {
      return point_card.points !== 0
    }).groupBy(function(point_card) {
      return point_card.name
    }).map(function(cards, card_name) {
      return {
        name: card_name,
        count: _.size(cards),
        types: _.head(cards).types,
        point_variable: _.head(cards).point_variable,
        points: _.head(cards).points * _.size(cards)
      }
    }).value()
  }

  landmark_cards(player_cards) {
    return _.chain(this.game.landmarks).map((landmark) => {
      let landmark_card = ClassCreator.create(landmark.name)
      return {
        name: landmark_card.name(),
        types: landmark_card.type_class(),
        points: landmark_card.victory_points(player_cards, this.game),
        point_variable: landmark_card.point_variable(player_cards, this.game)
      }
    }).filter(function(point_card) {
      return point_card.points !== 0
    }).value()
  }

  deck_breakdown(player_cards) {
    return _.chain(player_cards).groupBy(function(card) {
      return card.name
    }).map(function(cards, card_name) {
      return {
        name: card_name,
        count: _.size(cards),
        types: _.head(cards).types
      }
    }).value()
  }

  card_score(point_cards) {
    return _.reduce(point_cards, function(total, point_card) {
      return point_card.points + total
    }, 0)
  }

  top_score() {
    return _.head(this.game.scores).points
  }

  winners() {
    let winners = _.filter(this.game.scores, (score) => {
      return score.points === this.top_score()
    })
    if (_.size(winners) > 1) {
      winners = this.tiebreaker(winners)
    }
    return _.map(winners, 'username').join(', ')
  }

  tiebreaker(top_scorers) {
    return _.reduce(top_scorers, function(winners, top_scorer) {
      if (_.isEmpty(winners) || top_scorer.turns === winners[0].turns) {
        winners.push(top_scorer)
      } else if (top_scorer.turns < winners[0].turns) {
        winners = [top_scorer]
      }
      return winners
    }, [])
  }
}