• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

patri9ck / durak / #5

16 Jan 2025 01:26PM UTC coverage: 39.958% (-4.1%) from 44.107%
#5

push

travis-pro

LaurinEngelen
Merge branch 'main' into improved-gui

# Conflicts:
#	README.md
#	src/main/scala/view/gui/Gui.scala

78 of 83 new or added lines in 14 files covered. (93.98%)

302 existing lines in 7 files now uncovered.

378 of 946 relevant lines covered (39.96%)

0.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.23
/src/main/scala/controller/base/BaseController.scala
1
package controller.base
2

3
import com.google.inject.{Inject, Singleton}
4
import controller.Controller
5
import controller.base.command.*
6
import model.*
7
import model.io.FileIo
8
import model.status.{Status, StatusBuilder}
9
import util.{Observable, UndoManager}
10

11
import scala.annotation.tailrec
12
import scala.util.Random
13

14
@Singleton
15
class BaseController @Inject()(val fileIo: FileIo) extends Controller {
16

17
  private val undoManager = UndoManager()
1✔
18
  var status: Status = Status()
1✔
19

20
  def drawFromStack(statusBuilder: StatusBuilder): StatusBuilder = {
1✔
21
    val start = statusBuilder.getPassed.orElse(statusBuilder.byTurn(Turn.FirstlyAttacking)).map(statusBuilder.getPlayers.indexOf).get
1✔
22

23
    var updatedStack = statusBuilder.getStack
1✔
24
    var updatedPlayers = statusBuilder.getPlayers
1✔
25

26
    for (step <- statusBuilder.getPlayers.indices) {
1✔
27
      val index = (start - step + statusBuilder.getPlayers.size) % statusBuilder.getPlayers.size
1✔
28
      val player = statusBuilder.getPlayers(index)
1✔
29
      val amount = statusBuilder.getAmount - player.cards.length
1✔
30

31
      if (player.turn != Turn.Watching && amount > 0) {
1✔
32
        val (draw, remaining) = updatedStack.splitAt(amount)
1✔
33

34
        updatedStack = remaining
35
        updatedPlayers = updatedPlayers.updated(index, player.copy(cards = player.cards ++ draw))
1✔
36
      }
1✔
37
    }
38

39
    statusBuilder
40
      .setStack(updatedStack)
1✔
41
      .setPlayers(updatedPlayers)
1✔
42
      .removePassed()
1✔
43
  }
44

45
  def hasFinished(finished: Player, statusBuilder: StatusBuilder): Boolean = {
1✔
46
    if (finished.turn == Turn.Finished) {
1✔
47
      return true
48
    }
1✔
49

50
    if (finished.turn == Turn.FirstlyAttacking || finished.turn == Turn.SecondlyAttacking) {
1✔
51
      return statusBuilder.getStack.isEmpty && finished.cards.isEmpty
1✔
52
    }
1✔
53

54
    if (finished.turn == Turn.Defending) {
1✔
55
      return statusBuilder.getStack.isEmpty && statusBuilder.getUndefended.isEmpty && finished.cards.isEmpty
1✔
56
    }
1✔
57

58
    false
59
  }
60

61
  def finish(finished: Player, statusBuilder: StatusBuilder): StatusBuilder = {
1✔
62
    val updated = finished.copy(turn = Turn.Finished)
63

64
    var updatedStatusBuilder = statusBuilder.setPlayers(updatePlayers(statusBuilder.getPlayers, finished, updated))
1✔
65

66
    if (finished.turn == Turn.Defending) {
1✔
67
      updatedStatusBuilder = statusBuilder
68
        .setPlayers(chooseNextAttacking(updatedStatusBuilder.getPlayers, updated))
1✔
69
        .resetRound
1✔
70
    } else if (finished.turn == Turn.FirstlyAttacking && statusBuilder.byTurn(Turn.SecondlyAttacking).isEmpty || finished.turn == Turn.SecondlyAttacking) {
1✔
71
      updatedStatusBuilder = statusBuilder.setTurn(Turn.Defending)
1✔
72
    } else {
1✔
73
      updatedStatusBuilder = statusBuilder.setTurn(Turn.SecondlyAttacking)
1✔
74
    }
75

76
    updatedStatusBuilder
77
  }
78

79
  def chooseNextAttacking(players: List[Player], previous: Player): List[Player] =
1✔
80
    chooseAttacking(players, (players.indexOf(previous) - 1 + players.size) % players.size)
1✔
81

82
  def chooseAttacking(players: List[Player], index: Int): List[Player] = {
1✔
83
    players.zipWithIndex.map { case (player, idx) =>
1✔
84
      if (player.turn == Turn.Finished) {
1✔
85
        player
86
      } else {
1✔
87
        @tailrec
88
        def findPreviousActive(currentIndex: Int, steps: Int): Int = {
1✔
89
          if (steps == 0) currentIndex
1✔
90
          else {
1✔
91
            val prevIndex = (currentIndex - 1 + players.length) % players.length
1✔
92
            if (players(prevIndex).turn == Turn.Finished)
1✔
93
              findPreviousActive(prevIndex, steps)
1✔
94
            else
95
              findPreviousActive(prevIndex, steps - 1)
1✔
96
          }
97
        }
98

99
        val firstlyAttackingIndex = index
100
        val defendingIndex = findPreviousActive(firstlyAttackingIndex, 1)
1✔
101
        val secondlyAttackingIndex = findPreviousActive(defendingIndex, 1)
1✔
102

103
        if (idx == firstlyAttackingIndex) {
1✔
104
          player.copy(turn = Turn.FirstlyAttacking)
105
        } else if (idx == defendingIndex) {
1✔
106
          player.copy(turn = Turn.Defending)
107
        } else if (idx == secondlyAttackingIndex) {
1✔
108
          player.copy(turn = Turn.SecondlyAttacking)
109
        } else {
1✔
110
          player.copy(turn = Turn.Watching)
111
        }
112
      }
113
    }
114
  }
115

116
  def updatePlayers(players: List[Player], old: Player, updated: Player): List[Player] = {
1✔
117
    players.map { player =>
1✔
118
      if (old == player) {
1✔
119
        updated
120
      } else {
1✔
121
        player
122
      }
123
    }
124
  }
125

UNCOV
126
  override def initialize(amount: Int, names: List[String]): Unit = {
×
UNCOV
127
    initialize(amount, names, Random.shuffle(names).head)
×
128
  }
129

130
  override def initialize(amount: Int, names: List[String], attacking: String): Unit = {
×
131
    undoManager.doStep(InitializeCommand(this, amount, names, attacking))
×
132

UNCOV
133
    notifySubscribers()
×
134
  }
135

UNCOV
136
  override def deny(): Unit = {
×
137
    undoManager.doStep(DenyCommand(this))
×
138

UNCOV
139
    notifySubscribers()
×
140
  }
141

UNCOV
142
  override def pickUp(): Unit = {
×
143
    undoManager.doStep(PickUpCommand(this))
×
144

UNCOV
145
    notifySubscribers()
×
146
  }
147

UNCOV
148
  override def attack(card: Card): Unit = {
×
149
    undoManager.doStep(AttackCommand(this, card))
×
150

UNCOV
151
    notifySubscribers()
×
152
  }
153

154
  override def canAttack(card: Card): Boolean = {
1✔
155
    status.defended.isEmpty && status.undefended.isEmpty
1✔
156
      || status.used.exists(_.rank == card.rank)
1✔
157
      || status.defended.exists(_.rank == card.rank)
1✔
158
      || status.undefended.exists(_.rank == card.rank)
1✔
159
  }
160

UNCOV
161
  override def defend(used: Card, undefended: Card): Unit = {
×
UNCOV
162
    undoManager.doStep(DefendCommand(this, used, undefended))
×
163

UNCOV
164
    notifySubscribers()
×
165
  }
166

167
  override def canDefend(used: Card, undefended: Card): Boolean = {
1✔
168
    if (used.beats(undefended)) {
1✔
169
      return true
170
    }
1✔
171

172
    if (used.suit == status.trump.get.suit) {
1✔
173
      return undefended.suit != status.trump.get.suit
1✔
174
    }
1✔
175

176
    false
177
  }
178

179
  override def current: Option[Player] = byTurn(status.turn)
1✔
180

181
  override def byTurn(turn: Turn): Option[Player] = {
1✔
182
    status.players.find(_.turn == turn)
1✔
183
  }
184

UNCOV
185
  override def undo(): Unit = {
×
UNCOV
186
    undoManager.undoStep()
×
187

UNCOV
188
    notifySubscribers()
×
189
  }
190

UNCOV
191
  override def redo(): Unit = {
×
192
    undoManager.redoStep()
×
193

UNCOV
194
    notifySubscribers()
×
195
  }
196

UNCOV
197
  override def load(): Unit = {
×
198
    undoManager.doStep(LoadCommand(this, fileIo))
×
199

UNCOV
200
    notifySubscribers()
×
201
  }
202

203

204
  override def save(): Unit = {
×
UNCOV
205
    undoManager.doStep(SaveCommand(this, fileIo))
×
206

UNCOV
207
    notifySubscribers()
×
208
  }
209

NEW
210
  override def isOver: Boolean = status.players.count(_.turn != Turn.Finished) == 1
×
211

212
  override def unbind(): Unit = {
×
213
    fileIo.unbind()
×
214
  }
215
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc