179 Commits

Author SHA1 Message Date
8645d4a219 test(base): added some tests to improve the coverage (#47)
Reviewed-on: #47
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-10-22 20:12:03 +02:00
c9e44bc604 fix(base): fixed logic regarding dog player detection (#46)
Reviewed-on: #46
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-10-22 20:11:21 +02:00
7dad9052f7 fix(base): no dogs after the first round (#45)
Stopped people from becoming dogs in the first round

Reviewed-on: #45
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-10-22 15:08:09 +02:00
65921c289a Merge pull request 'finished: webapplication-logic-refactor' (#44) from webapplication-logic-refactor into webapplication
Reviewed-on: #44
2025-10-22 13:08:02 +02:00
cc4bc9d93e Fix tie number selection logic to include highest allowed number 2025-10-22 10:34:49 +02:00
c43cc7cac0 Fixes 2025-10-22 08:20:47 +02:00
e17ab6a552 Various fixes 2025-10-22 08:16:22 +02:00
786204dfaf Rename PlayCardEvent to RequestCardEvent for clarity in event handling 2025-10-19 20:49:25 +02:00
026c666f03 Refactor game logic to track player input state; add CardPlayedEvent and update RoundEndEvent with trick count 2025-10-19 20:48:45 +02:00
6335cbee23 Fixed TUI refactor and logic errors 2025-10-19 13:30:25 +02:00
16857fe42c TUI finished 2025-10-18 01:28:42 +02:00
1a189ef0fe Add core game logic components and event handling for player actions 2025-10-17 20:39:57 +02:00
f9a6e6af05 Refactor player classes and enhance game logic; add tie handling and player input management 2025-10-17 16:25:46 +02:00
11834a7d44 Refactor match handling and session management; add player session functionality and update event handling in WebUI 2025-10-13 15:16:41 +02:00
51ef1f1607 Provided the match with an ID 2025-10-10 20:17:25 +02:00
7676dd8ed1 Add WebUI and ingame route, update Configuration and DefaultConfiguration for UI integration 2025-10-10 13:31:50 +02:00
51604124fd Add initial application structure with HomeController and routing 2025-10-09 13:25:05 +02:00
48cd4d3956 Add migration state XML files and update build configuration 2025-10-09 10:52:04 +02:00
d54f814192 Merge pull request 'Implemented Docker' (#43) from dockertest into master
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Reviewed-on: #43
2025-01-23 18:09:24 +01:00
05b6ebb2a8 DOCKER WORKS
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-23 17:46:18 +01:00
079e66a5cf Docker File update
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-23 17:00:18 +01:00
220c3d04ea Hotfix for Presentation
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-23 16:04:39 +01:00
a6e514880c Docker File implementation
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-23 13:05:10 +01:00
ec329df768 Hotfix for Presentation
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-23 09:38:18 +01:00
a856843fc3 Fixed Tests v2
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-20 14:08:19 +01:00
LQ63
f57537b3eb updated WinnerScreen
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-20 13:34:07 +01:00
6c825845ea Merge branch 'master' into FileIO
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-20 13:32:43 +01:00
5b37ebb01d Fixed Tests 2025-01-20 13:31:54 +01:00
2b63fbe88d Merge pull request 'FileIO' (#42) from FileIO into master
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Reviewed-on: #42
2025-01-20 13:04:14 +01:00
LQ63
0e31134dfb better winning screen
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-18 19:54:26 +01:00
LQ63
d0ab531e16 Finished Basic functionality for Gui. Includes working cut, (very) basic winning screen 2025-01-18 17:16:18 +01:00
b6812d1223 Fixed Dog Play + Savegames 2025-01-18 15:17:06 +01:00
LQ63
169537c479 Added new Stuff for the gui, added support for gamesaving and loading. 2025-01-17 09:18:10 +01:00
aad61fd10a Load File improvements 2025-01-17 09:09:53 +01:00
d9b1bb2186 Tie fixes 2025-01-17 09:00:10 +01:00
d48e3b1daa File ending support 2025-01-17 08:55:45 +01:00
fa8cab1d6f FileIO 2025-01-16 21:53:31 +01:00
a53f4dda46 Tried fixing CT 2025-01-16 21:09:21 +01:00
483991bdbb Reduced Critical Section 2025-01-16 21:06:58 +01:00
bd73997959 XML FileIO Done 2025-01-16 20:24:40 +01:00
3a41a4bb4d Current FileIO State 2025-01-16 18:59:04 +01:00
474de82cde Fixed GUI 2025-01-16 18:47:59 +01:00
d39e92cfa8 Fixed PersistenceManager 2025-01-16 18:04:13 +01:00
e1d7405f51 FileIO Impl 2025-01-16 17:49:41 +01:00
a33e404378 Start of fileio 2025-01-15 20:24:25 +01:00
LQ63
2e0d24adbd changed tests
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2025-01-14 21:13:57 +01:00
13199ddd74 Dependency Injection 2025-01-09 14:35:45 +01:00
3396355193 Config Version yay
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:54:29 +01:00
LQ63
111242a6aa removed comments
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:52:37 +01:00
LQ63
681fdd4253 Merge remote-tracking branch 'origin/Components' into Components
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:51:26 +01:00
LQ63
3fe3abe7fc removed comments 2024-12-20 10:51:09 +01:00
e0b7a68207 Components yayyy
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:48:46 +01:00
LQ63
67a590e544 Configuration
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:38:19 +01:00
LQ63
8742c5dc7a Mock PlayerQueue + startet config
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 10:33:20 +01:00
LQ63
7a46bed011 Mock PlayerQueue
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 09:30:34 +01:00
LQ63
9ecf91282e Merge remote-tracking branch 'origin/Components' into Components 2024-12-20 09:29:55 +01:00
LQ63
0526f99464 Mock PlayerQueue 2024-12-20 09:29:32 +01:00
95228cbd51 Current Component State
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-20 09:24:20 +01:00
LQ63
49e378b51a Merge remote-tracking branch 'origin/Components' into Components
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 21:44:10 +01:00
LQ63
3b6ec1cd1b interfaces finished 2024-12-19 21:43:44 +01:00
d21540bbe7 Componentiesierung round 2
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 21:26:11 +01:00
LQ63
05afe8b392 interfaces almost finished(comments missing)
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 21:24:49 +01:00
92e9149d55 Componentiesierung
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 21:03:43 +01:00
LQ63
5324a16cbe Started components
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 20:38:00 +01:00
LQ63
ecff0a318d Started components 2024-12-19 20:37:52 +01:00
LQ63
b8c5b7a389 Started components
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 20:37:23 +01:00
3c83fec6b5 Added Background
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-19 11:29:59 +01:00
a5f7f14d06 GUI changes
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 12:28:27 +01:00
4aafd01f92 GUI changes 2024-12-13 12:23:57 +01:00
LQ63
2fe0ddcf45 Merge remote-tracking branch 'origin/GUI' into GUI
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 12:18:25 +01:00
LQ63
e1c2d660f1 added RoundEnd and TrickEnd animations 2024-12-13 12:18:00 +01:00
648a1b2ce7 Main Menu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 11:54:33 +01:00
56cc2f1182 Changed Delays added FirstCard
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 11:31:03 +01:00
LQ63
f7b09a0bd9 fixed TrumpsuitMenu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 11:18:20 +01:00
LQ63
ed8356577c added PickTrumpsuit Menu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 11:07:53 +01:00
085002a35a Made cards playable
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 09:40:59 +01:00
c3dcca6bde PlayedCards implemented
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 09:33:59 +01:00
4e43e25437 Main updated
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 09:30:55 +01:00
9d5194bf1d Game updated 2024-12-13 09:30:41 +01:00
LQ63
f5250f401f rendered cards for gui
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 01:23:06 +01:00
30e395d649 Game GUI WIP
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 00:58:59 +01:00
597c0f196a Starting GUI integration
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 00:24:31 +01:00
33c14d3731 Fixed Main Menu spawning twice
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-13 00:16:19 +01:00
961f91eef8 Custom Input... 2024-12-13 00:14:57 +01:00
LQ63
bb51dcb69b gui update
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 23:40:18 +01:00
82c6398dab Added State handling
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 23:31:50 +01:00
LQ63
b0e44b6690 started GUI
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 23:21:25 +01:00
0aae73f530 Fixed GUI Thread problems
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 23:02:18 +01:00
de63e94342 Changed Logic:
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Made UndoLogic Thread safe.
CustomThread optimized
2024-12-12 22:47:21 +01:00
b45efc91cb Merge branch 'WIPGUI' into GUI
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
# Conflicts:
#	src/main/scala/de/knockoutwhist/ui/gui/MainMenu.scala
2024-12-12 21:06:02 +01:00
fbd2f7a4dd Changed Logic:
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
UIs must now directly call the logic.
Removed Returnable Events
2024-12-12 21:00:13 +01:00
7f4a08c673 Custom Thread Support
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 15:50:12 +01:00
LQ63
34d9986979 added Return and checkmark button
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-12 01:35:17 +01:00
21e810b0da Agony and Pain
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 19:18:01 +01:00
LQ63
452810f9fa updated MainMenu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 19:17:49 +01:00
LQ63
bfcd207c38 updated MainMenu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 18:31:26 +01:00
3f958cf20a Added some animations added change child
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 18:08:54 +01:00
faecdf00be Added Main Menu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 17:31:53 +01:00
de9aa383ea Started Main Menu
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 17:04:33 +01:00
2fad96bb2c GUI start
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 15:46:20 +01:00
LQ63
aec723d243 updated sbt
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-11 15:27:44 +01:00
28c8f5850e Logic fixes (Tie a bit unstable)
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-06 11:23:16 +01:00
5e065c04c7 Fixed Undo - cause didn't work before
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-06 10:42:01 +01:00
4ef7333ef5 Fixed AI Logic - Fixed dog life (thx Leon)
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-06 10:18:01 +01:00
dddbd97362 Made everything immutable -> Tests severely broken, AI broken, Coverage very low (But it works)
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-05 23:19:06 +01:00
LQ63
46075fa356 changed RoundControl
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-05 19:36:16 +01:00
8553bf7941 Merge remote-tracking branch 'origin/commandPattern' into commandPattern
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-05 19:27:14 +01:00
1311cb20b8 Match immutable 2024-12-05 19:26:56 +01:00
LQ63
87e9d66f46 Player immutable
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-05 19:23:16 +01:00
9edea7082d CustomPlayerQueue clone 2024-12-05 18:20:44 +01:00
0a4d725669 Immutable + Command Pattern
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-12-05 18:15:41 +01:00
9e9941cd70 Merge pull request 'Hotfix Code Coverage' (#41) from development into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #41
2024-11-29 12:31:41 +01:00
0ce277fe33 Merge branch 'master' into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-29 12:29:05 +01:00
LQ63
4573f4c2a6 Coverage 100%
Some checks reported errors
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-29 12:28:23 +01:00
LQ63
9c5947a18f Merge pull request 'Patterns implemented' (#40) from development into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #40
2024-11-29 12:20:06 +01:00
337d27abbf Added second Iterator
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-29 12:16:02 +01:00
4e8a8a958d Merge remote-tracking branch 'origin/development' into playerpattern
Some checks reported errors
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-29 12:05:37 +01:00
a9871e6038 Removed Code Smell 2024-11-29 12:05:06 +01:00
0a37a583f7 Merge pull request 'Patterns implemented' (#39) from playerpattern into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #39
2024-11-29 12:01:55 +01:00
9da9477763 Merge branch 'master' into playerpattern 2024-11-29 12:01:37 +01:00
36286be458 100% coverage
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-29 11:58:37 +01:00
ca3ffa4ba7 Builder Pattern finished
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-29 11:55:09 +01:00
LQ63
82d7ab94b4 Started Builder
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-29 11:44:39 +01:00
7487a0edf6 Merge remote-tracking branch 'origin/playerpattern' into playerpattern
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
# Conflicts:
#	src/test/scala/de/knockoutwhist/player/AITests.scala
2024-11-29 11:19:30 +01:00
6b3b702ffe Added Code Coverage 2024-11-29 11:18:26 +01:00
LQ63
1cc776bfaa optimized imports
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-29 10:40:20 +01:00
LQ63
1e50a40868 startet tests for ai
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 22:55:38 +01:00
LQ63
fe4f6a7f46 fixed current test to match the new patterns
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 22:23:52 +01:00
LQ63
7f69e6f444 optimized imports
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 20:51:57 +01:00
LQ63
e589f36c22 changed constructor access
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 20:38:06 +01:00
LQ63
33e15bca27 finished AILogic implementation, added Factory Pattern and Template Pattern
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 20:31:08 +01:00
8d7420358d Added AI Logic
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 19:11:51 +01:00
LQ63
522a1cfa23 started pattern creation
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-28 18:06:22 +01:00
LQ63
39336120e1 Merge pull request 'Full functional Code' (#37) from development into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #37
Reviewed-by: Janis <janis-e@gmx.de>
2024-11-21 22:27:12 +01:00
LQ63
777e4c2d6b Merge pull request 'reduced smelly code' (#36) from lastsmellfix into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #36
2024-11-21 22:10:44 +01:00
LQ63
d75c2cf62a merged two if's to reduce smelly code
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 22:08:23 +01:00
LQ63
9191012aaf Merge pull request 'FixedIssues' (#35) from fixissues into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #35
2024-11-21 22:01:04 +01:00
LQ63
1a7e48864b Fixed TuiMain
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 21:54:16 +01:00
LQ63
6ef9e95cb8 Changed TuiMain so that it isn't smelly anymore
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 21:50:26 +01:00
c94e37f63e Reduced complexity
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 21:29:16 +01:00
LQ63
be4e00af44 Reduced smelly code
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 21:17:53 +01:00
LQ63
ebea37aece Merge remote-tracking branch 'refs/remotes/origin/development' into fixissues
# Conflicts:
#	src/main/scala/de/knockoutwhist/control/PlayerControl.scala
#	src/main/scala/de/knockoutwhist/control/RoundControl.scala
#	src/main/scala/de/knockoutwhist/control/TrickControl.scala
2024-11-21 21:16:04 +01:00
b9e896fc0b Merge pull request 'Reduced Test Time by a lot' (#34) from test-improvements into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #34
2024-11-21 20:57:49 +01:00
a0e2eadc93 Reduced Test Time by a lot
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 19:42:14 +01:00
LQ63
47169bdb8b changed a name to match the given regex by sonarqube
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 12:25:22 +01:00
3a6473379d Merge pull request 'README.md aktualisiert' (#33) from janis-patch-1 into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #33
2024-11-19 20:24:21 +01:00
11dab537ba README.md aktualisiert 2024-11-19 20:24:14 +01:00
3a246b4419 Merge pull request 'README.md aktualisiert' (#32) from janis-patch-1 into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #32
2024-11-19 15:18:43 +01:00
d940620049 README.md aktualisiert 2024-11-19 15:18:33 +01:00
4d86baa6d9 Merge pull request 'README.md aktualisiert' (#31) from janis-patch-1 into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #31
2024-11-19 15:16:34 +01:00
14ef284229 README.md aktualisiert
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-19 15:16:23 +01:00
d73c4c0577 Merge pull request 'development' (#30) from development into master
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #30
2024-11-18 10:50:36 +01:00
0b5bfc69e5 Merge pull request 'Coveralls' (#29) from coverall into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #29
2024-11-18 10:45:23 +01:00
ad0494f0ba Merge remote-tracking branch 'origin/development' into coverall
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
# Conflicts:
#	project/plugins.sbt
2024-11-18 10:42:48 +01:00
355addf8a5 Merge pull request 'eventsystem' (#28) from eventsystem into development
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #28
2024-11-18 10:38:50 +01:00
efd5e0fae6 Coveralls
All checks were successful
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-18 10:37:44 +01:00
8cb5012c8f Remove unnecessary method
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:35:55 +01:00
LQ63
6304ce44b9 Changed GameplayTests
Some checks reported errors
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:35:39 +01:00
ee44f6940b Added more tests
Some checks reported errors
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:33:26 +01:00
LQ63
0c6de4b86f Added Tests for Events
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:00:48 +01:00
094afc5631 Added DelayHandlerTests
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:55:16 +01:00
fabb2f3580 Merge pull request 'Implemented Assembly Pipeline' (#27) from development into master
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Reviewed-on: #27
2024-11-15 11:38:39 +01:00
bb0ad578be Merge pull request 'Pipeline Test' (#26) from pipelineWorks into development
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Reviewed-on: #26
2024-11-15 11:38:13 +01:00
ce5efd7a05 Pipeline Test
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:35:26 +01:00
174fe3c993 Fixed Tests
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:17:34 +01:00
03ab3f3650 Improved Code Coverage
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:13:00 +01:00
LQ63
a7f839d01c Changed Tests according to the new structure.
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:35:12 +01:00
a7e84b9815 Fixed DelayHandler
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:14:22 +01:00
4c7fb1c8bc Fixed EventHandler
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:09:11 +01:00
ef1eb29fe3 Merge remote-tracking branch 'origin/eventsystem' into eventsystem
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 18:06:51 +01:00
LQ63
619cbcfaac Changed structure of Control
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 18:06:33 +01:00
fe46690fb4 Added a clear console for the main menu 2024-11-14 17:31:32 +01:00
24bfd7925c Splitted Logic between UI and Logic
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 17:28:39 +01:00
11c0b61933 Rearranged everything Data Structure got no more logic
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 16:47:38 +01:00
0cc7756556 Switcher Handlers over to one
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Added UI Trait
Renamed Controls
Deleted old Logic
2024-11-14 12:31:17 +01:00
153d028959 Added a delay handler
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-13 22:04:41 +01:00
LQ63
177ec3772e Added more Events, finished GenericPlayerControl. Startet with GenericMatchControl
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-13 18:07:28 +01:00
c397d079bf Reworked Events
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
Created Events
2024-11-13 13:45:19 +01:00
a99182beaf WIP - Event System
Some checks failed
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-11 23:14:56 +01:00
218 changed files with 6512 additions and 1893 deletions

6
.idea/copilot.data.migration.agent.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.ask.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.edit.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

1
DOCKER-COMMANDS Normal file
View File

@@ -0,0 +1 @@
docker run -it --rm -e DISPLAY=host.docker.internal:0 -v /tmp/.X11-unix:/tmp/.X11-unix knockout:v1

35
Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM sbtscala/scala-sbt:eclipse-temurin-jammy-22_36_1.10.0_3.4.2
WORKDIR /knockout
ENV DEBIAN_FRONTEND=noninterface
RUN apt-get update && apt-get install -y curl gnupg2 x11-apps
RUN apt-get update && apt-get install -y \
libxext6 \
libxrender1 \
libxtst6 \
libxi6 \
libxrandr2 \
libgtk-3-0 \
xorg
RUN curl -sL https://dlcdn.apache.org/sbt/debian/sbt-1.9.4.deb -o sbt.deb
RUN dpkg -i sbt.deb || apt-get install -f -y
ENV DEBIAN_FRONTEND=dialog
RUN sbt update
ENV DISPLAY=:99
ENV SBT_OPTS="-Xms512M -Xmx1536M -Xss2M -XX:MaxMetaspaceSize=512M"
ENV _JAVA_OPTIONS="-Djava.security.policy=applet.policy -Dprism.order=sw"
VOLUME /tmp/.X11-unix
COPY . /knockout
RUN sbt compile
CMD ["sh", "-c", "sbt run"]

View File

@@ -1,4 +1,4 @@
# Knock-out Whist
# Knock-out Whist [![Coverage Status](https://coveralls.io/repos/github/16Janis12/KnockOutWhist/badge.svg?branch=master)](https://coveralls.io/github/16Janis12/KnockOutWhist?branch=master) [![Build-Status-Master](https://teamcity.janis-eccarius.de/guestAuth/app/rest/builds/buildType:(id:KnockOutWhist_BuildAndTest),branch:master/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=16Janis12_KnockOutWhist&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=16Janis12_KnockOutWhist)
## About the Game
Knock-out Whist is a trick-taking card game designed for 2 to 7 players. It uses a standard 52-card deck.
The objective is to win tricks. Players who fail to win at least one trick in a round are eliminated.
@@ -29,14 +29,17 @@ If they win a trick, they re-enter the game in the following round. When this ha
## About the project
This project is a Scala implementation of the Knock-out Whist card game. It is maintained by 2 students of the HTWG Konstanz.
Last stable build: [![Build-Status-Master](https://teamcity.janis-eccarius.de/guestAuth/app/rest/builds/buildType:(id:KnockOutWhist_BuildAndTest),branch:master/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master)
Last stable build: [![Build-Status-Master](https://teamcity.janis-eccarius.de/app/rest/builds/buildType:id:KnockOutWhist_BuildAndTest/statusIcon.svg)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master)
Last development build: [![Build-Status-Dev](https://teamcity.janis-eccarius.de/guestAuth/app/rest/builds/buildType:(id:KnockOutWhist_BuildAndTest),branch:development/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=development)
Last development build: [![Build-Status-Dev](https://teamcity.janis-eccarius.de/app/rest/builds/buildType:id:KnockOutWhist_BuildAndTest,branch:name:development/statusIcon.svg)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=development)
Gitea: [https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist)
GitHub (Mirror): [https://github.com/16Janis12/KnockOutWhist](https://github.com/16Janis12/KnockOutWhist)
SonarQube: [https://sonarcloud.io/project/overview?id=16Janis12_KnockOutWhist](https://sonarcloud.io/project/overview?id=16Janis12_KnockOutWhist)
Coveralls: [https://coveralls.io/github/16Janis12/KnockOutWhist](https://coveralls.io/github/16Janis12/KnockOutWhist)
## Licence
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

View File

@@ -1,30 +0,0 @@
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "3.5.1"
Compile/mainClass := Some("de.knockoutwhist.KnockOutWhist")
name := "KnockOutWhist"
version := {
val major = sys.env.getOrElse("MAJOR_VERSION", "0")
val minor = sys.env.getOrElse("MINOR_VERSION", "0")
val buildNR = sys.env.getOrElse("BUI_COUNTER", "1")
s"$major.$minor.$buildNR"
}
organization := "de.knockoutwhist"
lazy val root = (project in file("."))
.settings(
name := "Projekt-zu-SE"
)
libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.18"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.18" % "test"
Test / testOptions += Tests.Filter(_.equals("de.knockoutwhist.TestSequence"))
coverageEnabled := true
coverageFailOnMinimum := true
coverageMinimumStmtTotal := 85
coverageMinimumBranchTotal := 100

View File

@@ -1 +0,0 @@
sbt.version = 1.10.2

View File

@@ -1 +0,0 @@
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.1")

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,5 +0,0 @@
import de.knockoutwhist.cards.CardManager
val originalDeck = List(CardManager.cardContainer)
CardManager.shuffleAndReset()
val shuffledDeck = CardManager.cardContainer

View File

@@ -1,24 +1,46 @@
package de.knockoutwhist
import de.knockoutwhist.control.MatchControl
import de.knockoutwhist.control.text.TextMatchControl
import com.google.inject.{Guice, Injector}
import de.knockoutwhist.components.Configuration
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.di.KnockOutConfigurationModule
import de.knockoutwhist.utils.events.EventListener
object KnockOutWhist {
val matchControl: MatchControl = TextMatchControl
/*
Debug mode:
Debug mode
- Disables the random shuffle of the cards
*/
private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = true
private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = false
private var _config: Option[Configuration] = None
def config: Configuration = _config.get
def DEBUG_MODE = DEBUG_MODE_VAR
def debugmode: Boolean = DEBUG_MODE_VAR
def main(args: Array[String]): Unit = {
if(!matchControl.initial()) throw new IllegalStateException("Game could not be started.")
val injector: Injector = Guice.createInjector(KnockOutConfigurationModule())
val config: Configuration = injector.getInstance(classOf[Configuration])
entry(config)
}
def entry(configuration: Configuration): Unit = {
_config = Some(configuration)
val baseLogic = BaseGameLogic(configuration)
for (handler <- configuration.listener) baseLogic.addListener(handler)
ControlThread.start()
for (ui <- configuration.uis) {
if (!ui.initial(baseLogic)) throw new IllegalStateException(s"${ui.getClass.getName} could not be started.")
ui match {
case eventListener: EventListener => baseLogic.addListener(eventListener)
case _ =>
}
}
}
}

View File

@@ -0,0 +1,9 @@
//start from 0, stop at 9 inclusive
for (i <- 0 until 10){
println("Hi " + i)
}
//or start from 0, stop at 9 inclusive
for (i <- 0 to 9){
println("Hi " + i)
}

View File

@@ -1,8 +1,5 @@
package de.knockoutwhist.cards
import de.knockoutwhist.cards.CardValue.Ten
import de.knockoutwhist.cards.Suit
enum Suit(identifier: String):
def cardType(): String = identifier
@@ -34,34 +31,15 @@ enum CardValue(identifier: String):
end CardValue
case class Card(cardValue: CardValue, suit: Suit) {
override def toString: String = s"$cardValue of $suit"
def cardColour(suit: Suit): String = suit match {
case Suit.Hearts | Suit.Diamonds => Console.RED
case Suit.Clubs | Suit.Spades => Console.BLACK
override def canEqual(that: Any): Boolean = that.isInstanceOf[Card]
override def equals(obj: Any): Boolean = obj match {
case that: Card =>
this.cardValue == that.cardValue &&
this.suit == that.suit
case _ => false
}
def renderAsString(): Vector[String] = {
if (cardValue == Ten) {
return Vector(
s"┌─────────┐",
s"${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}",
"│ │",
s"${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET}",
"│ │",
s"${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}",
s"└─────────┘"
)
}
Vector(
s"┌─────────┐",
s"${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}",
"│ │",
s"${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET}",
"│ │",
s"${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}",
s"└─────────┘"
)
}
override def toString: String = s"$cardValue of $suit"
//Combined String
}

View File

@@ -1,50 +1,25 @@
package de.knockoutwhist.cards
import de.knockoutwhist.KnockOutWhist
trait CardManager {
import scala.collection.mutable.ListBuffer
import scala.util.Random
def cardContainer: List[Card]
object CardManager {
def shuffleAndReset(): Unit
def resetOrder(): Unit
var cardContainer: List[Card] = {
val cc = ListBuffer[Card]()
for (suit <- Suit.values) {
for (cardValue <- CardValue.values) {
cc += Card(cardValue, suit)
}
}
cc.toList
}
private var currentIdx = 0
def shuffleAndReset(): Unit = {
cardContainer = Random.shuffle(cardContainer)
currentIdx = 0
}
def nextCard(): Card
def resetOrder(): Unit = {
cardContainer = cardContainer.sortBy(c => (c.suit.ordinal, c.cardValue.ordinal))
currentIdx = 0
}
def remainingCards: Int = cardContainer.size - currentIndx
def nextCard(): Card = {
val card = cardContainer(currentIdx)
if (currentIdx + 1 > 51) {
throw new IndexOutOfBoundsException("Trying to access card 53(out of bounds)")
} else {
currentIdx += 1
card
}
}
def removeCards(amount: Int): List[Card]
def createHand(amount: Int = 7): Hand = {
val hand = ListBuffer[Card]()
for (_ <- 1 to amount) {
hand += nextCard()
}
Hand(hand.toList)
}
def createHand(amount: Int = 7): Hand
def grabSpecificCard(card: Card): Card
def currentIndx: Int
def setState(cc: List[Card], currentIndex: Int): Unit
}

View File

@@ -1,7 +1,5 @@
package de.knockoutwhist.cards
import scala.collection.mutable.ListBuffer
case class Hand(cards: List[Card]) {
def removeCard(card: Card): Hand = {
@@ -19,11 +17,5 @@ case class Hand(cards: List[Card]) {
def hasTrumpSuit(trumpSuit: Suit): Boolean = {
cards.exists(_.suit == trumpSuit)
}
def renderAsString() : List[String] = {
val cardStrings = cards.map(_.renderAsString())
val zipped = cardStrings.transpose
zipped.map(_.mkString(" "))
}
}

View File

@@ -0,0 +1,68 @@
package de.knockoutwhist.cards.base
import de.knockoutwhist.cards.*
import scala.collection.mutable.ListBuffer
import scala.util.Random
class CardBaseManager extends CardManager {
override def setState(cc: List[Card], currentIndex: Int): Unit = {
this.cc = cc
this.currentIdx = currentIndex
}
override def cardContainer: List[Card] = cc
private var cc: List[Card] = {
val cc = ListBuffer[Card]()
for (suit <- Suit.values) {
for (cardValue <- CardValue.values) {
cc += Card(cardValue, suit)
}
}
cc.toList
}
private var currentIdx = 0
override def currentIndx: Int = currentIdx
override def shuffleAndReset(): Unit = {
cc = Random.shuffle(cc)
currentIdx = 0
}
override def resetOrder(): Unit = {
cc = cc.sortBy(c => (c.suit.ordinal, c.cardValue.ordinal))
currentIdx = 0
}
override def nextCard(): Card = {
val card = cc(currentIdx)
if (currentIdx + 1 > 51) {
throw new IndexOutOfBoundsException("Trying to access card 53(out of bounds)")
} else {
currentIdx += 1
card
}
}
override def removeCards(amount: Int): List[Card] = {
val removedCards = ListBuffer[Card]()
for (_ <- 0 to amount) {
removedCards += nextCard()
}
removedCards.toList
}
override def createHand(amount: Int = 7): Hand = {
val hand = ListBuffer[Card]()
for (_ <- 1 to amount) {
hand += nextCard()
}
Hand(hand.toList)
}
override def grabSpecificCard(card: Card): Card = {
cc.filter(c => c.suit == card.suit && c.cardValue == card.cardValue).head
}
}

View File

@@ -0,0 +1,23 @@
package de.knockoutwhist.cards.stub
import de.knockoutwhist.cards.*
object StubCardManager extends CardManager {
override def cardContainer: List[Card] = List()
override def shuffleAndReset(): Unit = {}
override def resetOrder(): Unit = {}
override def nextCard(): Card = Card(CardValue.Ace, Suit.Clubs)
override def removeCards(amount: Int): List[Card] = List(Card(CardValue.Ace, Suit.Clubs))
override def createHand(amount: Int): Hand = Hand(List(Card(CardValue.Ace, Suit.Clubs)))
override def grabSpecificCard(card: Card): Card = card
override def currentIndx: Int = -1
override def setState(cc: List[Card], currentIndex: Int): Unit = {}
}

View File

@@ -0,0 +1,18 @@
package de.knockoutwhist.components
import de.knockoutwhist.cards.CardManager
import de.knockoutwhist.persistence.formats.FileFormatter
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.ui.UI
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.events.EventListener
trait Configuration {
def cardManager: CardManager
def fileFormatter: FileFormatter
def uis: Set[UI]
def listener: Set[EventListener]
def createRightQueue(players: Array[AbstractPlayer], start: Int = 0): CustomPlayerQueue[AbstractPlayer]
}

View File

@@ -0,0 +1,38 @@
package de.knockoutwhist.components
import com.google.inject.Guice
import de.knockoutwhist.cards.CardManager
import de.knockoutwhist.di.{KnockOutConfigurationModule, KnockOutLogicModule}
import de.knockoutwhist.persistence.formats.FileFormatter
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.ui.UI
import de.knockoutwhist.ui.gui.GUIMain
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.baseQueue.QueueBuilder
import de.knockoutwhist.utils.events.EventListener
import scala.language.postfixOps
class DefaultConfiguration extends Configuration {
private val injector = Guice.createInjector(KnockOutLogicModule(), KnockOutConfigurationModule())
override def cardManager: CardManager = injector.getInstance(classOf[CardManager])
override def fileFormatter: FileFormatter = injector.getInstance(classOf[FileFormatter])
override def uis: Set[UI] = Set[UI](
TUIMain(),
GUIMain()
)
override def listener: Set[EventListener] = Set[EventListener](
utils.DelayHandler
)
override def createRightQueue(players: Array[AbstractPlayer], start: Int): CustomPlayerQueue[AbstractPlayer] = {
val builder = injector.getInstance(classOf[QueueBuilder])
builder.setStart(start)
builder.setPlayer(players)
builder.build()
}
}

View File

@@ -0,0 +1,13 @@
package de.knockoutwhist.control
import de.knockoutwhist.utils.CustomThread
object ControlThread extends CustomThread {
setName("ControlThread")
def isControlThread: Boolean = Thread.currentThread().equals(ControlThread)
override def instance: CustomThread = ControlThread
}

View File

@@ -0,0 +1,37 @@
package de.knockoutwhist.control
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundResult
import de.knockoutwhist.control.sublogic.{PersistenceManager, PlayerInputLogic, PlayerTieLogic, UndoManager}
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.events.EventHandler
trait GameLogic extends EventHandler with SnapshottingGameLogic {
def createSession(): Unit
def endSession(): Unit
def createMatch(players: List[AbstractPlayer]): Match
def controlMatch(): Unit
def controlRound(): Unit
def endRound(winner: AbstractPlayer, roundResult: RoundResult): Match
def controlTrick(): Unit
def endTrick(): Round
def controlPlayerPlay(): Unit
def providePlayersWithCards(): Unit
def isWaitingForInput: Boolean
def playerInputLogic: PlayerInputLogic
def playerTieLogic: PlayerTieLogic
def undoManager: UndoManager
def persistenceManager: PersistenceManager
def getCurrentState: GameState
def getCurrentMatch: Option[Match]
def getCurrentRound: Option[Round]
def getCurrentTrick: Option[Trick]
def getCurrentPlayer: Option[AbstractPlayer]
def getPlayerQueue: Option[CustomPlayerQueue[AbstractPlayer]]
}

View File

@@ -0,0 +1,12 @@
package de.knockoutwhist.control
enum GameState {
case MainMenu
case Lobby
case InGame
case SelectTrump
case TieBreak
case FinishedMatch
}

View File

@@ -1,24 +0,0 @@
package de.knockoutwhist.control
import de.knockoutwhist.rounds.{Match, Round, Trick}
trait MatchControl {
def initial(): Boolean
def start(): Unit
def playerControl: PlayerControl
/**
* Start the next round
* @return the next round or null if the match is over
*/
def nextRound(matchImpl: Match): Round
/**
* Start the next trick
* @return the last trick or null if the round is over
*/
def nextTrick(roundImpl: Round): Trick
}

View File

@@ -1,16 +0,0 @@
package de.knockoutwhist.control
import de.knockoutwhist.cards.{Card, Suit}
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.Round
trait PlayerControl {
def playCard(player: Player): Card
def dogplayCard(player: Player, round: Round): Option[Card]
def determineWinnerTie(players: List[Player]): Player
def pickNextTrumpsuit(player: Player): Suit
def showCards(player: Player): Boolean
def showWon(player: Player, round: Round): String
}

View File

@@ -0,0 +1,13 @@
package de.knockoutwhist.control
trait SnapshottingGameLogic {
def createSnapshot(): LogicSnapshot[this.type]
}
trait LogicSnapshot[T <: SnapshottingGameLogic] {
def restore(logic: T): Unit
}

View File

@@ -0,0 +1,407 @@
package de.knockoutwhist.control.controllerBaseImpl
import de.knockoutwhist.cards.{Card, CardManager}
import de.knockoutwhist.components.Configuration
import de.knockoutwhist.control.GameState.*
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, RoundResult, RoundUtil, TrickUtil}
import de.knockoutwhist.control.controllerBaseImpl.sublogic.{BasePersistenceManager, BasePlayerInputLogic, BasePlayerTieLogic, BaseUndoManager}
import de.knockoutwhist.control.sublogic.{PersistenceManager, PlayerInputLogic, PlayerTieLogic}
import de.knockoutwhist.control.{GameLogic, GameState, LogicSnapshot}
import de.knockoutwhist.events.global.*
import de.knockoutwhist.events.global.tie.TieEvent
import de.knockoutwhist.events.player.ReceivedHandEvent
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.persistence.MethodEntryPoint.{ControlMatch, ControlRound}
import de.knockoutwhist.player.{AbstractPlayer, PlayerData}
import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.events.EventHandler
import java.util.UUID
import scala.util.Random
/*
Main game logic controller
*/
final class BaseGameLogic(val config: Configuration) extends EventHandler with GameLogic {
//Constants
val ID: UUID = UUID.randomUUID()
//Logics
val playerTieLogic: PlayerTieLogic = BasePlayerTieLogic(this)
val playerInputLogic: PlayerInputLogic = BasePlayerInputLogic(this)
val undoManager: BaseUndoManager = BaseUndoManager(this)
val persistenceManager: PersistenceManager = BasePersistenceManager(this)
//Game State
private[control] var state: GameState = GameState.MainMenu
//Variables
private[control] var cardManager: Option[CardManager] = Some(config.cardManager)
private[control] var currentMatch: Option[Match] = None
private[control] var currentRound: Option[Round] = None
private[control] var currentTrick: Option[Trick] = None
private[control] var currentPlayer: Option[AbstractPlayer] = None
private[control] var playerQueue: Option[CustomPlayerQueue[AbstractPlayer]] = None
override def createSession(): Unit = {
cardManager = Some(config.cardManager)
currentMatch = None
currentRound = None
currentTrick = None
currentPlayer = None
playerQueue = None
invoke(GameStateChangeEvent(state, Lobby))
state = Lobby
}
override def createMatch(players: List[AbstractPlayer]): Match = {
val matchImpl = Match(totalplayers = players, playersIn = players)
currentMatch = Some(matchImpl)
matchImpl
}
override def controlMatch(): Unit = {
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
val matchImpl = currentMatch.get
persistenceManager.update(ControlMatch)
if (matchImpl.isOver) {
//Winner is the last person in the playersIn list
val winner = matchImpl.playersIn.head
invoke(GameStateChangeEvent(state, FinishedMatch))
state = FinishedMatch
invoke(MatchEndEvent(winner))
} else {
if (matchImpl.roundlist.isEmpty) {
if (cardManager.isEmpty) throw new IllegalStateException("No card manager set")
val cardManagerImpl = cardManager.get
val firstCard = cardManagerImpl.nextCard()
val newRound = RoundUtil.createRound(firstCard.suit, true)
providePlayersWithCards()
val randomPlayer: Int = 1//Random.nextInt(matchImpl.playersIn.size)
playerQueue = Some(config.createRightQueue(matchImpl.playersIn.toArray, randomPlayer))
matchImpl.playersIn.foreach(player => {invoke(ReceivedHandEvent(player))})
currentRound = Some(newRound)
invoke(NewRoundEvent())
invoke(DelayEvent(500))
controlRound()
return
}
currentMatch = Some(matchImpl.setNumberOfCards(matchImpl.numberofcards - 1))
providePlayersWithCards()
matchImpl.playersIn.foreach(player => {invoke(ReceivedHandEvent(player))})
//Check if the last round had a winner
val lastRound = matchImpl.roundlist.last
if (lastRound.winner.isEmpty)
throw new IllegalStateException("Last round had no winner")
val lastWinner = lastRound.winner.get
//Create new player queue starting with last round winner
playerQueue = Some(config.createRightQueue(
matchImpl.playersIn.toArray,
matchImpl.playersIn.indexOf(lastWinner)
))
invoke(GameStateChangeEvent(state, SelectTrump))
state = SelectTrump
invoke(TrumpSelectEvent(lastWinner))
playerInputLogic.requestTrumpSuit(lastWinner)
}
}
override def controlRound(): Unit = {
if (state != InGame)
invoke(GameStateChangeEvent(state, InGame))
state = InGame
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
val matchImpl = currentMatch.get
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
val roundImpl = currentRound.get
persistenceManager.update(ControlRound)
if (MatchUtil.isRoundOver(matchImpl, roundImpl)) {
val roundResult: RoundResult = RoundUtil.finishRound(roundImpl, matchImpl)
if (roundResult.isTie) {
invoke(GameStateChangeEvent(state, TieBreak))
state = TieBreak
invoke(TieEvent(roundResult.winners))
invoke(DelayEvent(2000))
playerTieLogic.handleTie(roundResult)
return
}
val newMatch = endRound(roundResult.winners.head, roundResult)
currentMatch = Some(newMatch)
controlMatch()
} else {
invoke(NewTrickEvent())
invoke(DelayEvent(1000))
val trick = Trick()
currentTrick = Some(trick)
controlTrick()
}
}
override def endRound(winner: AbstractPlayer, roundResult: RoundResult): Match = {
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
var matchImpl = currentMatch.get
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
val roundImpl = currentRound.get
//Create final round snapshot
val resultingRound = Round(
trumpSuit = roundImpl.trumpSuit,
firstRound = roundImpl.firstRound,
tricklist = roundImpl.tricklist,
winner = Some(winner)
)
invoke(RoundEndEvent(winner, roundResult.tricked.filter(
rp => rp.player == winner
).map(rp => rp.amountOfTricks).sum))
invoke(DelayEvent(2000))
if (roundResult.notTricked.nonEmpty && !roundImpl.firstRound) {
if (matchImpl.dogLife) {
invoke(ShowPlayersOutEvent(roundResult.notTricked))
invoke(DelayEvent(2000))
matchImpl = matchImpl.updatePlayersIn(matchImpl.playersIn.filterNot(roundResult.notTricked.contains(_)))
} else {
invoke(ShowDogsEvent(roundResult.notTricked))
invoke(DelayEvent(2000))
matchImpl = matchImpl.setDogLife()
// Make players dogs
roundResult.notTricked.foreach(player => {
player.setDogLife()
})
}
}
roundResult.tricked.foreach(player => {
player.player.resetDogLife()
})
matchImpl.addRound(resultingRound)
}
override def controlTrick(): Unit = {
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
val matchImpl = currentMatch.get
if (playerQueue.isEmpty) throw new IllegalStateException("No player queue set")
val queueImpl = playerQueue.get
if (currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
val trickImpl = currentTrick.get
persistenceManager.update(ControlRound)
if (TrickUtil.isOver(matchImpl, queueImpl)) {
val newRound = endTrick()
if (newRound.tricklist.isEmpty || newRound.tricklist.last.winner.isEmpty) throw new IllegalStateException("Trick has no winner after ending trick")
val winner = newRound.tricklist.last.winner.get
currentRound = Some(newRound)
invoke(TrickEndEvent(winner))
invoke(DelayEvent(2000))
queueImpl.resetAndSetStart(winner)
controlRound()
} else {
val playerImpl = queueImpl.nextPlayer()
currentPlayer = Some(playerImpl)
controlPlayerPlay()
}
}
override def endTrick(): Round = {
if (currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
val trickImpl = currentTrick.get
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
val roundImpl = currentRound.get
val resultTrick = TrickUtil.finishTrick(trickImpl, roundImpl)
val resultingTrick = Trick(
cards = trickImpl.cards,
winner = Some(resultTrick.winner),
firstCard = trickImpl.firstCard
)
roundImpl.addTrick(resultingTrick)
}
override def controlPlayerPlay(): Unit = {
if (currentPlayer.isEmpty) throw new IllegalStateException("No current player set")
val playerImpl = currentPlayer.get
if (playerImpl.currentHand().isEmpty) {
controlTrick()
return
}
val handImpl = playerImpl.currentHand().get
if (handImpl.cards.isEmpty) {
controlTrick()
return
}
invoke(TurnEvent(playerImpl))
playerInputLogic.requestCard(playerImpl)
}
override def isWaitingForInput: Boolean = {
if (state == InGame || state == SelectTrump) {
playerInputLogic.isWaitingForInput
} else if (state == TieBreak) {
playerTieLogic.isWaitingForInput
} else {
false
}
}
//
override def providePlayersWithCards(): Unit = {
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
val matchImpl = currentMatch.get
if (cardManager.isEmpty) throw new IllegalStateException("No card manager set")
val cardManagerImpl = cardManager.get
cardManagerImpl.shuffleAndReset()
val handSize = matchImpl.numberofcards
matchImpl.playersIn.foreach(player => {
val hand = if (!player.isInDogLife) {
cardManagerImpl.createHand(handSize)
} else {
cardManagerImpl.createHand(1)
}
player.provideHand(hand)
})
}
// Getters
override def getCurrentState: GameState = state
override def getCurrentMatch: Option[Match] = currentMatch
override def getCurrentRound: Option[Round] = currentRound
override def getCurrentTrick: Option[Trick] = currentTrick
override def getCurrentPlayer: Option[AbstractPlayer] = currentPlayer
override def getPlayerQueue: Option[CustomPlayerQueue[AbstractPlayer]] = playerQueue
//Snapshotting
override def createSnapshot(): LogicSnapshot[BaseGameLogic.this.type] = {
BaseGameLogicSnapshot(this).asInstanceOf[LogicSnapshot[BaseGameLogic.this.type]]
}
override def endSession(): Unit = {
//TODO Return to main menu
System.exit(0)
}
}
class BaseGameLogicSnapshot(
val savedState: GameState,
//Card Manager
val cardContainer: Option[List[Card]],
val cardIndex: Option[Int],
val currentMatch: Option[Match],
val currentRound: Option[Round],
val currentTrick: Option[Trick],
val currentPlayer: Option[AbstractPlayer],
//Custom Player Queue
val playerIndex: Option[Int],
val players: Option[List[AbstractPlayer]],
val playerStates: Map[UUID, PlayerData]
) extends LogicSnapshot[BaseGameLogic] {
def this(gameLogic: BaseGameLogic) = {
this(
gameLogic.state,
gameLogic.cardManager.map(cm => cm.cardContainer),
gameLogic.cardManager.map(cm => cm.currentIndx),
gameLogic.currentMatch,
gameLogic.currentRound,
gameLogic.currentTrick,
gameLogic.currentPlayer,
gameLogic.playerQueue.map(pq => pq.currentIndex),
gameLogic.playerQueue.map(pq => pq.duplicate().toList),
gameLogic.currentMatch match {
case Some(m) => m.totalplayers.map(p => (p.id, p.generatePlayerData())).toMap
case None => Map.empty[UUID, PlayerData]
}
)
}
override def restore(logic: BaseGameLogic): Unit = {
logic.state = savedState
//Card Manager
if (logic.cardManager.isDefined) {
val cardManagerImpl = logic.cardManager.get
if (cardContainer.isDefined && cardIndex.isDefined)
cardManagerImpl.setState(cardContainer.get, cardIndex.get)
} else {
if (cardContainer.isDefined && cardIndex.isDefined) {
val newCardManager = logic.config.cardManager
newCardManager.setState(cardContainer.get, cardIndex.get)
logic.cardManager = Some(newCardManager)
}
}
logic.currentMatch = currentMatch
logic.currentRound = currentRound
logic.currentTrick = currentTrick
logic.currentPlayer = currentPlayer
//Custom Player Queue
if (logic.playerQueue.isDefined) {
if (players.isDefined && playerIndex.isDefined)
logic.playerQueue = Some(logic.config.createRightQueue(
players.get.toArray,
playerIndex.get
))
} else {
if (players.isDefined && playerIndex.isDefined)
logic.playerQueue = Some(logic.config.createRightQueue(
players.get.toArray,
playerIndex.get
))
}
//Player States
logic.currentMatch match {
case Some(m) =>
m.totalplayers.foreach(player => {
val dataOpt = playerStates.get(player.id)
if (dataOpt.isDefined)
player.receivePlayerData(dataOpt.get)
})
case None => //Do nothing
}
}
}

View File

@@ -0,0 +1,42 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.controllerBaseImpl.sublogic.BasePlayerTieLogic
import de.knockoutwhist.control.sublogic.{PersistenceManager, PlayerTieLogic}
import de.knockoutwhist.control.{GameLogic, LogicSnapshot}
import de.knockoutwhist.persistence.formats.JSONFormatter
import de.knockoutwhist.persistence.{MatchSnapshot, MethodEntryPoint}
import de.knockoutwhist.rounds.{Match, Round, Trick}
class BasePersistenceManager(val gameLogic: BaseGameLogic) extends PersistenceManager {
private var currentSnapshot: MatchSnapshot = MatchSnapshot()
override def update(methodEntryPoint: MethodEntryPoint): MatchSnapshot = {
currentSnapshot = currentSnapshot
.withMethodEntryPoint(methodEntryPoint)
.withGameLogicSnapShot(gameLogic.createSnapshot().asInstanceOf[LogicSnapshot[GameLogic]])
currentSnapshot
}
override def saveFile(path: String): Unit = {
KnockOutWhist.config.fileFormatter.writeToFile(currentSnapshot, path + "." + KnockOutWhist.config.fileFormatter.fileEnding)
}
override def loadFile(path: String): Unit = {
currentSnapshot = KnockOutWhist.config.fileFormatter.readFromFile(path + "." + KnockOutWhist.config.fileFormatter.fileEnding)
if currentSnapshot.entryPoint.isEmpty then
throw new IllegalStateException("Loaded snapshot does not contain an entry point!")
currentSnapshot.gameLogicSnapShot.foreach(_.restore(gameLogic))
currentSnapshot.entryPoint.get match {
case MethodEntryPoint.ControlMatch =>
gameLogic.controlMatch()
case MethodEntryPoint.ControlRound =>
gameLogic.controlRound()
case MethodEntryPoint.ControlTrick =>
gameLogic.controlTrick()
}
}
}

View File

@@ -0,0 +1,82 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic
import de.knockoutwhist.cards.{Card, Suit}
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundUtil
import de.knockoutwhist.control.sublogic.PlayerInputLogic
import de.knockoutwhist.events.global.{CardPlayedEvent, TrumpSelectedEvent}
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.player.AbstractPlayer
final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLogic {
private var _waitingForInput: Boolean = false
override def requestTrumpSuit(player: AbstractPlayer): Unit = {
_waitingForInput = true
gameLogic.invoke(RequestTrumpSuitEvent(player))
}
override def receivedTrumpSuit(suit: Suit): Unit = {
val newRound = RoundUtil.createRound(suit)
_waitingForInput = false
gameLogic.currentRound = Some(newRound)
gameLogic.invoke(TrumpSelectedEvent(suit))
gameLogic.controlRound()
}
override def requestCard(player: AbstractPlayer): Unit = {
_waitingForInput = true
gameLogic.invoke(RequestCardEvent(player))
}
override def receivedCard(card: Card): Unit = {
if (gameLogic.currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
val trickImpl = gameLogic.currentTrick.get
if (gameLogic.currentPlayer.isEmpty) throw new IllegalStateException("No current player set")
val player = gameLogic.currentPlayer.get
_waitingForInput = false
val newTrick = if (trickImpl.firstCard.isEmpty) {
trickImpl
.setfirstcard(card)
.addCard(card, player)
} else {
trickImpl
.addCard(card, player)
}
player.removeCard(card)
gameLogic.currentTrick = Some(newTrick)
gameLogic.invoke(CardPlayedEvent(player, newTrick))
gameLogic.controlTrick()
}
override def receivedDog(dog: Option[Card]): Unit = {
if (gameLogic.currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
val trickImpl = gameLogic.currentTrick.get
if (gameLogic.currentPlayer.isEmpty) throw new IllegalStateException("No current player set")
val player = gameLogic.currentPlayer.get
_waitingForInput = false
if (dog.isDefined) {
val newTrick = if (trickImpl.firstCard.isEmpty) {
trickImpl
.setfirstcard(dog.get)
.addCard(dog.get, player)
} else {
trickImpl
.addCard(dog.get, player)
}
player.removeCard(dog.get)
gameLogic.currentTrick = Some(newTrick)
}
gameLogic.controlTrick()
}
override def isWaitingForInput: Boolean = _waitingForInput
}

View File

@@ -0,0 +1,166 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic
import de.knockoutwhist.cards.Card
import de.knockoutwhist.control.LogicSnapshot
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{ResultPlayer, RoundResult}
import de.knockoutwhist.control.sublogic.PlayerTieLogic
import de.knockoutwhist.events.global.tie.*
import de.knockoutwhist.events.player.RequestTieChoiceEvent
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.player.AbstractPlayer
final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic {
private[control] var roundResult: Option[RoundResult] = None
private[control] var tiedPlayers: List[AbstractPlayer] = Nil
private[control] var tieBreakerIndex: Int = -1
private[control] var lastNumber = -1
private[control] var selectedCard: Map[AbstractPlayer, Card] = Map.empty
private var _waitingForInput: Boolean = false
override def handleTie(roundResult: RoundResult): Unit = {
this.roundResult = Some(roundResult)
tiedPlayers = roundResult.winners
tieBreakerIndex = -1
lastNumber = 0
selectedCard = Map.empty
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
gameLogic.cardManager.get.shuffleAndReset()
handleNextTieBreakerPlayer()
}
override def handleNextTieBreakerPlayer(): Unit = {
tieBreakerIndex += 1
if(tieBreakerIndex >= 0 && tieBreakerIndex < tiedPlayers.size) {
requestTieChoice(currentTiePlayer())
} else {
// All players have selected their tie-breaker cards
// Find the highest card among selected cards
gameLogic.invoke(TieAllPlayersSelectedEvent())
gameLogic.invoke(DelayEvent(2000))
gameLogic.invoke(TieShowPlayerCardsEvent())
val winningEntry = selectedCard.values.maxBy(_.cardValue.ordinal)
val winners = selectedCard.filter((_, card) => card == winningEntry).keySet.toList
gameLogic.invoke(TieWinningPlayersEvent(winners))
gameLogic.invoke(DelayEvent(2000))
if (winners.size > 1) {
gameLogic.invoke(TieTieEvent(winners))
// Still a tie, handle again
tiedPlayers = winners
tieBreakerIndex = -1
lastNumber = 0
selectedCard = Map.empty
gameLogic.cardManager.get.shuffleAndReset()
handleNextTieBreakerPlayer()
return
}
// Tie-breaker resolved
roundResult = None
tiedPlayers = Nil
lastNumber = -1
tieBreakerIndex = -1
selectedCard = Map.empty
val winner = winners.head
// Inform game logic about the winner
}
}
override def currentTiePlayer(): AbstractPlayer = {
tiedPlayers(tieBreakerIndex)
}
override def requestTieChoice(player: AbstractPlayer): Unit = {
_waitingForInput = true
gameLogic.invoke(TieTurnEvent(player))
gameLogic.invoke(RequestTieChoiceEvent(player, highestAllowedNumber()))
}
/**
* Called when a player has selected a tie-breaker card
* @param number the index of the selected card
*/
override def receivedTieBreakerCard(number: Int): Unit = {
val player = tiedPlayers(tieBreakerIndex)
val highestNumber = highestAllowedNumber()
if (number < 0 || number > highestNumber)
throw new IllegalArgumentException(s"Selected number $number is out of allowed range (0 to $highestNumber)")
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
_waitingForInput = false
val cardManager = gameLogic.cardManager.get
val card = cardManager.removeCards(number).last
selectedCard += (player -> card)
handleNextTieBreakerPlayer()
}
override def highestAllowedNumber(): Int = {
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
val remainingCards = gameLogic.cardManager.get.remainingCards
// The highest allowed number is total cards minus the number of tied players already selected
// This ensures that each tied player can select a unique card
remainingCards - (tiedPlayers.size - selectedCard.size - 1)
}
override def isWaitingForInput: Boolean = _waitingForInput
override def createSnapshot(): LogicSnapshot[BasePlayerTieLogic.this.type] = BasePlayerTieLogicSnapshot(this).asInstanceOf[LogicSnapshot[BasePlayerTieLogic.this.type]]
// Getter
override def getRoundResult: Option[RoundResult] = roundResult
override def getTiedPlayers: List[AbstractPlayer] = tiedPlayers
override def getTieBreakerIndex: Int = tieBreakerIndex
override def getLastNumber: Int = lastNumber
override def getSelectedCard: Map[AbstractPlayer, Card] = selectedCard
}
class BasePlayerTieLogicSnapshot(
//Round result
val winners: List[AbstractPlayer],
val tricked: List[ResultPlayer],
val notTricked: List[AbstractPlayer],
val tiedPlayers: List[AbstractPlayer],
val tieBreakerIndex: Int,
val lastNumber: Int,
val selectedCard: Map[AbstractPlayer, Card]
) extends LogicSnapshot[BasePlayerTieLogic] {
def this(logic: BasePlayerTieLogic) = {
this(
logic.roundResult.map(_.winners).getOrElse(Nil),
logic.roundResult.map(_.tricked).getOrElse(Nil),
logic.roundResult.map(_.notTricked).getOrElse(Nil),
logic.tiedPlayers,
logic.tieBreakerIndex,
logic.lastNumber,
logic.selectedCard
)
}
override def restore(logic: BasePlayerTieLogic): Unit = {
if (winners.nonEmpty || tricked.nonEmpty || notTricked.nonEmpty) {
logic.roundResult = Some(RoundResult(winners, tricked, notTricked))
} else {
logic.roundResult = None
}
logic.tiedPlayers = tiedPlayers
logic.tieBreakerIndex = tieBreakerIndex
logic.lastNumber = lastNumber
logic.selectedCard = selectedCard
}
}

View File

@@ -0,0 +1,51 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.sublogic.UndoManager
import de.knockoutwhist.undo.{Command, UndoneException}
class BaseUndoManager(gameLogic: BaseGameLogic) extends UndoManager {
private var undoStack: List[Command] = Nil
private var redoStack: List[Command] = Nil
override def doStep(command: Command): Unit = {
redoStack = Nil
undoStack = command :: undoStack
command.doStep(gameLogic)
}
override def undoStep(): Unit = {
ControlThread.runLater {
undoStack match {
case Nil => false
case head :: stack =>
undoStack = stack
redoStack = head :: redoStack
try {
head.undoStep(gameLogic)
} catch {
case _: UndoneException =>
}
}
}
}
override def redoStep(): Unit = {
ControlThread.runLater {
redoStack match {
case Nil => false
case head :: stack =>
redoStack = stack
undoStack = head :: undoStack
try {
head.doStep(gameLogic)
} catch {
case _: UndoneException =>
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
import de.knockoutwhist.rounds.{Match, Round}
object MatchUtil {
private def remainingRounds(matchImpl: Match, roundImpl: Round): Int = {
//Find player with the most cards
matchImpl.numberofcards - roundImpl.tricklist.size
}
def isRoundOver(matchImpl: Match, roundImpl: Round): Boolean = {
//Find player with the most cards
remainingRounds(matchImpl, roundImpl) == 0
}
def dogNeedsToPlay(matchImpl: Match, roundImpl: Round): Boolean = {
remainingRounds(matchImpl, roundImpl) == 1
}
}

View File

@@ -0,0 +1,37 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
import de.knockoutwhist.cards.Card
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.{Round, Trick}
object PlayerUtil {
def canPlayCard(card: Card, round: Round, trick: Trick, player: AbstractPlayer): Boolean = {
if (trick.firstCard.isEmpty)
return true
val alternatives = alternativeCards(card, round, trick, player)
if (alternatives.nonEmpty) {
return false
}
true
}
def alternativeCards(card: Card, round: Round, trick: Trick, player: AbstractPlayer): List[Card] = {
if (trick.firstCard.isDefined) {
val firstCard = trick.firstCard.get
if (firstCard.suit != card.suit) {
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
if cardInHand.suit == firstCard.suit
yield cardInHand
if (round.trumpSuit == card.suit && alternatives.isEmpty) {
return Nil
}
if (alternatives.nonEmpty) {
return alternatives
}
}
}
Nil
}
}

View File

@@ -0,0 +1,43 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
import de.knockoutwhist.cards.Suit
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.{Match, Round}
object RoundUtil {
def createRound(trumpSuit: Suit, firstRound: Boolean = false): Round = {
Round(trumpSuit, firstRound)
}
def finishRound(round: Round, matchImpl: Match): RoundResult = {
val tricksMapped = round.tricklist
.map(t => t.winner)
.filter(t => t.isDefined)
.map(t => t.get)
.groupBy(identity).map((p, l) => (p, l.size))
val maxTricks = if (tricksMapped.isEmpty) 0 else tricksMapped.values.max
val winners = tricksMapped
.filter((p, i) => i == maxTricks)
.keys.toList
val trickedPlayers = tricksMapped.map((p, i) => ResultPlayer(p, i)).toList
val notTrickedPlayers = matchImpl.playersIn.filterNot(p => tricksMapped.keySet.contains(p))
RoundResult(winners, trickedPlayers, notTrickedPlayers)
}
def roundEndSnapshot(winner: AbstractPlayer, round: Round): Round = {
Round(
round.trumpSuit,
round.firstRound,
round.tricklist,
Some(winner)
)
}
}
case class RoundResult(winners: List[AbstractPlayer], tricked: List[ResultPlayer], notTricked: List[AbstractPlayer]) {
def isTie: Boolean = winners.size > 1
}
case class ResultPlayer(player: AbstractPlayer, amountOfTricks: Int)

View File

@@ -0,0 +1,13 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
import de.knockoutwhist.control.{GameLogic, LogicSnapshot}
object SnapshotUtil {
def generateSnapshots(gameLogic: GameLogic): (LogicSnapshot[? <: GameLogic], LogicSnapshot[? <: GameLogic]) = {
val gameLogicSnapshot = gameLogic.createSnapshot()
val playerTieLogicSnapshot = gameLogic.playerTieLogic.createSnapshot()
(gameLogicSnapshot, playerTieLogicSnapshot.asInstanceOf[LogicSnapshot[? <: GameLogic]])
}
}

Some files were not shown because too many files have changed in this diff Show More