From e4bb1066b1b55c265ed773c46bdcff68a6814c87 Mon Sep 17 00:00:00 2001 From: Soeren Domroes Date: Mon, 2 Mar 2026 10:54:47 +0100 Subject: [PATCH 01/19] build(gradle): start gradle upgrade --- backend | 2 +- build.gradle.kts | 41 +++++++++++++----------- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/backend b/backend index e2a37e1..c390729 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit e2a37e18b3ca4929003ed2d56e43d492ee9caff9 +Subproject commit c390729b49d319d56841d39cb6fe6c14044d0aa1 diff --git a/build.gradle.kts b/build.gradle.kts index c249214..2f8ad20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,22 +1,23 @@ import org.gradle.internal.os.OperatingSystem import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.gradle.process.ExecOperations import java.util.Properties -val minJavaVersion = JavaVersion.VERSION_11 +val minJavaVersion = JavaVersion.VERSION_17 plugins { - val minJavaVersion = JavaVersion.VERSION_11 // Declared twice because plugins block has its own scope + val minJavaVersion = JavaVersion.VERSION_17 // Declared twice because plugins block has its own scope require(JavaVersion.current() >= minJavaVersion) { "Building requires at least JDK $minJavaVersion - please look into the README" } application - kotlin("jvm") version "1.9.25" + kotlin("jvm") version "2.3.0" id("idea") id("org.openjfx.javafxplugin") version "0.1.0" - id("com.github.johnrengelman.shadow") version "6.1.0" + id("com.gradleup.shadow") version "9.3.1" - id("com.github.ben-manes.versions") version "0.47.0" - id("se.patrikerdes.use-latest-versions") version "0.2.18" + id("com.github.ben-manes.versions") version "0.53.0" + id("se.patrikerdes.use-latest-versions") version "0.2.19" } idea { @@ -44,7 +45,7 @@ version = try { println("Current version: $version (Java version: ${JavaVersion.current()})") application { - mainClassName = "sc.gui.GuiAppKt" // needs shadow-update which needs gradle update to 7.0 + mainClass.set("sc.gui.GuiAppKt") // needs shadow-update which needs gradle update to 7.0 } repositories { @@ -92,14 +93,14 @@ tasks { } } withType { - kotlinOptions { - jvmTarget = minJavaVersion.toString() - freeCompilerArgs = listOf("-Xjvm-default=all") + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(minJavaVersion.toString())) + freeCompilerArgs.addAll("-Xjvm-default=all") } } withType { - manifest.attributes["Main-Class"] = application.mainClassName + manifest.attributes["Main-Class"] = application.mainClass.get() } javafx { @@ -113,7 +114,7 @@ tasks { } shadowJar { - destinationDirectory.set(buildDir) + destinationDirectory.set(layout.buildDirectory.asFile.get()) archiveClassifier.set( "${ OperatingSystem.current().familyName.replace( @@ -140,24 +141,28 @@ tasks { run.configure { dependsOn(backend.task(":server:makeRunnable")) - workingDir(buildDir.resolve("run")) + workingDir(layout.buildDirectory.asFile.get().resolve("run")) doFirst { workingDir.mkdirs() } args = System.getProperty("args", "").split(" ") } - val release by creating { + val release by registering { dependsOn(clean, check) group = "distribution" description = "Create and push a tagged commit matching the backend version" doLast { val desc = project.properties["m"]?.toString() ?: throw InvalidUserDataException("Das Argument -Pm=\"Beschreibung dieser Version\" wird benötigt") - exec { commandLine("git", "add", "CHANGELOG.md") } - exec { commandLine("git", "commit", "-m", "release: v$versionFromBackend") } - exec { commandLine("git", "tag", versionFromBackend, "-m", desc) } - exec { commandLine("git", "push", "--follow-tags", "--recurse-submodules=on-demand") } + val processHelper: (Array) -> Unit = { args -> + val process = ProcessBuilder(*args).start() + process.waitFor() + } + processHelper(arrayOf("git", "add", "CHANGELOG.md")) + processHelper(arrayOf("git", "commit", "-m", "release: v$versionFromBackend")) + processHelper(arrayOf("git", "tag", versionFromBackend, "-m", desc)) + processHelper(arrayOf("git", "push", "--follow-tags", "--recurse-submodules=on-demand")) } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c51cbf1..e69d040 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 016aa1857cd56b2d21b405581e72d9c7eb62c3f7 Mon Sep 17 00:00:00 2001 From: xeruf Date: Mon, 2 Mar 2026 13:34:24 +0100 Subject: [PATCH 02/19] build(gradle): adjust for gradle 8 --- backend | 2 +- build.gradle.kts | 28 +++++++++++++--------------- settings.gradle.kts | 14 +------------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/backend b/backend index c390729..186accd 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit c390729b49d319d56841d39cb6fe6c14044d0aa1 +Subproject commit 186accdafea70f32a72a048e00a39f0226882b02 diff --git a/build.gradle.kts b/build.gradle.kts index 2f8ad20..380a0da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,11 @@ import org.gradle.internal.os.OperatingSystem +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.gradle.process.ExecOperations import java.util.Properties -val minJavaVersion = JavaVersion.VERSION_17 +val minJavaVersion = JavaVersion.VERSION_11 plugins { - val minJavaVersion = JavaVersion.VERSION_17 // Declared twice because plugins block has its own scope + val minJavaVersion = JavaVersion.VERSION_11 // Declared twice because plugins block has its own scope require(JavaVersion.current() >= minJavaVersion) { "Building requires at least JDK $minJavaVersion - please look into the README" } @@ -14,7 +14,7 @@ plugins { kotlin("jvm") version "2.3.0" id("idea") id("org.openjfx.javafxplugin") version "0.1.0" - id("com.gradleup.shadow") version "9.3.1" + id("com.gradleup.shadow") version "9.1.0" id("com.github.ben-manes.versions") version "0.53.0" id("se.patrikerdes.use-latest-versions") version "0.2.19" @@ -45,7 +45,7 @@ version = try { println("Current version: $version (Java version: ${JavaVersion.current()})") application { - mainClass.set("sc.gui.GuiAppKt") // needs shadow-update which needs gradle update to 7.0 + mainClass.set("sc.gui.GuiAppKt") } repositories { @@ -73,9 +73,10 @@ dependencies { implementation("io.github.oshai", "kotlin-logging-jvm", "6.0.9") // TODO version 7 with kotlin 2 implementation("software-challenge", "server") + implementation("software-challenge", "plugin2023") implementation("software-challenge", "plugin2024") implementation("software-challenge", "plugin2025") - implementation("software-challenge", "plugin") + implementation("software-challenge", "plugin2026") if(debug) implementation("com.tangorabox", "component-inspector-fx", "1.1.0") @@ -94,7 +95,7 @@ tasks { } withType { compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(minJavaVersion.toString())) + jvmTarget.set(JvmTarget.fromTarget(minJavaVersion.toString())) freeCompilerArgs.addAll("-Xjvm-default=all") } } @@ -155,14 +156,11 @@ tasks { doLast { val desc = project.properties["m"]?.toString() ?: throw InvalidUserDataException("Das Argument -Pm=\"Beschreibung dieser Version\" wird benötigt") - val processHelper: (Array) -> Unit = { args -> - val process = ProcessBuilder(*args).start() - process.waitFor() - } - processHelper(arrayOf("git", "add", "CHANGELOG.md")) - processHelper(arrayOf("git", "commit", "-m", "release: v$versionFromBackend")) - processHelper(arrayOf("git", "tag", versionFromBackend, "-m", desc)) - processHelper(arrayOf("git", "push", "--follow-tags", "--recurse-submodules=on-demand")) + + providers.exec { commandLine("git", "add", "CHANGELOG.md") } + providers.exec { commandLine("git", "commit", "-m", "release: v$versionFromBackend") } + providers.exec { commandLine("git", "tag", versionFromBackend, "-m", desc) } + providers.exec { commandLine("git", "push", "--follow-tags", "--recurse-submodules=on-demand") } } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index bebc179..0ebfb25 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,16 +1,4 @@ rootProject.name = "software-challenge-gui" includeBuild("backend/gradle/custom-tasks") -includeBuild("backend") { - // https://publicobject.com/2021/03/11/includebuild - dependencySubstitution { - substitute(module("software-challenge:plugin2024")) - .with(project(":plugin")) - substitute(module("software-challenge:plugin2025")) - .with(project(":plugin2025")) - substitute(module("software-challenge:plugin")) - .with(project(":plugin2026")) - substitute(module("software-challenge:server")) - .with(project(":server")) - } -} \ No newline at end of file +includeBuild("backend") \ No newline at end of file From efb026d1bf1451e09633f67a243c37ed807b7c55 Mon Sep 17 00:00:00 2001 From: xeruf Date: Tue, 3 Mar 2026 10:31:09 +0100 Subject: [PATCH 03/19] build(gradle): document debug flag --- build.gradle.kts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 380a0da..dcb3f33 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,6 +56,7 @@ repositories { maven("https://jitpack.io") } +// ./gradlew run -Pdebug for debug tools and logging val debug = project.hasProperty("debug") dependencies { @@ -78,8 +79,10 @@ dependencies { implementation("software-challenge", "plugin2025") implementation("software-challenge", "plugin2026") - if(debug) + if(debug) { + // hold Ctrl to view component hierarchy and bounds implementation("com.tangorabox", "component-inspector-fx", "1.1.0") + } } tasks { @@ -109,7 +112,8 @@ tasks { val mods = mutableListOf( "javafx.base", "javafx.controls", "javafx.fxml", "javafx.web", "javafx.media", "javafx.swing" - ) // included because of tornadofx already + ) + // included because of tornadofx already // if(debug) mods.addAll(listOf("javafx.swing")) modules = mods } From a3d1a083a25e95d20c3b2550e5f43b25cb5ae3b5 Mon Sep 17 00:00:00 2001 From: xeruf Date: Tue, 3 Mar 2026 10:33:37 +0100 Subject: [PATCH 04/19] build: update docs and flows --- .github/workflows/gradle.yml | 2 +- README.md | 19 ++++--------------- backend | 2 +- build.gradle.kts | 14 +++++++------- 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b3cc2e0..5d95dc4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -7,7 +7,7 @@ jobs: matrix: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs os: [ubuntu-latest, windows-latest, macos-latest] - jdk: [11] + jdk: [17, 24] steps: - uses: actions/checkout@v4 with: diff --git a/README.md b/README.md index b141e81..bbd46d0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Software-Challenge Logo Grafischer Spieleserver der Software-Challenge Germany ![.github/workflows/gradle.yml](https://github.com/software-challenge/gui/workflows/.github/workflows/gradle.yml/badge.svg) Dies ist die Grafische Oberfläche für die Software-Challenge Germany, -seit Saison 2020/21 in Kotlin TornadoFX aufbauend auf JavaFX. +seit Saison 2020/21 in Kotlin TornadoFX, aufbauend auf JavaFX. Nutzerdokumentation: https://docs.software-challenge.de/server.html @@ -23,7 +23,7 @@ wird der Server auf diesem Port Verbindungen von Spielern erwarten. ### Kollaboration Unsere Commit-Messages folgen dem Muster `type(scope): summary` -(siehe [Karma Runner Konvention](http://karma-runner.github.io/6.2/dev/git-commit-msg.html)), +(siehe [Karma Runner Konvention](http://karma-runner.github.io/6.4/dev/git-commit-msg.html)), wobei die gängigen Scopes in [.dev/scopes.txt](.dev/scopes.txt) definiert werden. Nach dem Klonen mit git sollte dazu der hook aktiviert werden: @@ -31,7 +31,7 @@ Nach dem Klonen mit git sollte dazu der hook aktiviert werden: Um bei den Branches die Übersicht zu behalten, sollten diese ebenfalls nach der Konvention benannt werden, -z. B. könnte ein Branch mit einem Release-Fix für Gradle `chore/gradle/release-fix` heißen +z. B. könnte ein Branch mit einem Release-Fix für Gradle `build/gradle/release-fix` heißen und ein Branch, der ein neues Login-Feature zur GUI hinzufügt, `feat/gui-login`. Wenn die einzelnen Commits eines Pull Requests eigenständig funktionieren, @@ -40,15 +40,4 @@ ansonsten (gerade bei experimentier-Branches) ein squash merge, wobei der Titel des Pull Requests der Commit-Message entsprechen sollte. Detaillierte Informationen zu unserem Kollaborations-Stil -findet ihr in der [Kull Konvention](https://kull.jfischer.org). - -### Java-Versionen und Abhängigkeiten - -Aktuell können die Backend-docs nur mit JDK 8 gebaut werden, -dieses Projekt braucht jedoch für [tornadofx](https://github.com/edvin/tornadofx2) -mindestens Java 11. -Daher müssen die Releases separat gebaut werden. - -Tornadofx wird leider seit einigen Jahren nicht mehr entwickelt. -Wir schauen gerade wie es da weitergeht. -Eventuell ein eigener Fork. +findet ihr in der [Kull Konvention](https://kull.jfischer.org). \ No newline at end of file diff --git a/backend b/backend index 186accd..d5ae974 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 186accdafea70f32a72a048e00a39f0226882b02 +Subproject commit d5ae974203cd037a282a32e207ec9688daa50fd2 diff --git a/build.gradle.kts b/build.gradle.kts index dcb3f33..790bfc3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,9 +3,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.util.Properties -val minJavaVersion = JavaVersion.VERSION_11 +val minJavaVersion = JavaVersion.VERSION_17 plugins { - val minJavaVersion = JavaVersion.VERSION_11 // Declared twice because plugins block has its own scope + val minJavaVersion = JavaVersion.VERSION_17 // Declared twice because plugins block has its own scope require(JavaVersion.current() >= minJavaVersion) { "Building requires at least JDK $minJavaVersion - please look into the README" } @@ -14,7 +14,7 @@ plugins { kotlin("jvm") version "2.3.0" id("idea") id("org.openjfx.javafxplugin") version "0.1.0" - id("com.gradleup.shadow") version "9.1.0" + id("com.gradleup.shadow") version "9.3.2" id("com.github.ben-manes.versions") version "0.53.0" id("se.patrikerdes.use-latest-versions") version "0.2.19" @@ -63,15 +63,15 @@ dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - + + implementation(files("./gradle/tornadofx2-21e933fd41.jar")) // implementation("no.tornado", "tornadofx", "2.0.0-SNAPSHOT") { exclude("org.jetbrains.kotlin", "kotlin-reflect") } // implementation("com.github.software-challenge.tornadofx2", "tornadofx2", "2.0.0") // implementation("com.github.edvin", "tornadofx2", "master-SNAPSHOT") // implementation("com.github.edvin", "tornadofx2", "21e933fd41") - implementation(files("./gradle/tornadofx2-21e933fd41.jar")) - implementation("ch.qos.logback", "logback-classic", "1.5.18") - implementation("io.github.oshai", "kotlin-logging-jvm", "6.0.9") // TODO version 7 with kotlin 2 + implementation("ch.qos.logback", "logback-classic", "1.5.32") + implementation("io.github.oshai", "kotlin-logging-jvm", "8.0.01") implementation("software-challenge", "server") implementation("software-challenge", "plugin2023") From c8bf5e665916e4d0971811b6a991fae7e058eed8 Mon Sep 17 00:00:00 2001 From: xeruf Date: Tue, 3 Mar 2026 10:33:52 +0100 Subject: [PATCH 05/19] fix: code deprecations --- build.gradle.kts | 2 +- src/main/kotlin/sc/gui/AppStyle.kt | 2 +- src/main/kotlin/sc/gui/controller/ServerController.kt | 4 ++-- src/main/kotlin/sc/gui/view/AppView.kt | 4 ++-- src/main/kotlin/sc/gui/view/ControlView.kt | 4 ++-- src/main/kotlin/sc/gui/view/game/HuIBoard.kt | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 790bfc3..e4e09c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,7 +99,7 @@ tasks { withType { compilerOptions { jvmTarget.set(JvmTarget.fromTarget(minJavaVersion.toString())) - freeCompilerArgs.addAll("-Xjvm-default=all") + //freeCompilerArgs.addAll("-jvm-default=all") } } diff --git a/src/main/kotlin/sc/gui/AppStyle.kt b/src/main/kotlin/sc/gui/AppStyle.kt index 01acab4..7371528 100644 --- a/src/main/kotlin/sc/gui/AppStyle.kt +++ b/src/main/kotlin/sc/gui/AppStyle.kt @@ -22,7 +22,7 @@ class AppStyle: Stylesheet() { const val pieceOpacity = 1.0 - val fontSizeUnscaled = Font.getDefault().also { logger.debug("System Font: $it") }.size.pt + val fontSizeUnscaled = Font.getDefault().also { logger.debug { "System Font: $it" } }.size.pt val fontSizeRegular = fontSizeUnscaled * AppModel.scaling.value val fontSizeSmall = fontSizeRegular * 0.6 val fontSizeBig = fontSizeRegular * 1.5 diff --git a/src/main/kotlin/sc/gui/controller/ServerController.kt b/src/main/kotlin/sc/gui/controller/ServerController.kt index 0b8c741..f67f09c 100644 --- a/src/main/kotlin/sc/gui/controller/ServerController.kt +++ b/src/main/kotlin/sc/gui/controller/ServerController.kt @@ -1,7 +1,7 @@ package sc.gui.controller import ch.qos.logback.classic.LoggerContext -import ch.qos.logback.core.util.StatusPrinter +import ch.qos.logback.core.util.StatusPrinter2 import org.slf4j.LoggerFactory import sc.server.Configuration import sc.server.Lobby @@ -13,7 +13,7 @@ class ServerController : Controller() { fun startServer() { // output logback diagnostics to see if a logback.xml config was found val lc = LoggerFactory.getILoggerFactory() as LoggerContext - StatusPrinter.print(lc) + StatusPrinter2().print(lc) Configuration.loadServerProperties() Configuration.set(Configuration.SAVE_REPLAY, true) diff --git a/src/main/kotlin/sc/gui/view/AppView.kt b/src/main/kotlin/sc/gui/view/AppView.kt index 2098dda..396a469 100644 --- a/src/main/kotlin/sc/gui/view/AppView.kt +++ b/src/main/kotlin/sc/gui/view/AppView.kt @@ -29,13 +29,13 @@ class AppView: View("Software-Challenge Germany") { // TODO help menus keep disappearing and is offset menu(graphic = sochaIcon) { item("Beenden", "Shortcut+Q").action { - logger.debug("Quitting!") + logger.debug { "Quitting!" } Platform.exit() } item("Neues Spiel", "Shortcut+N") { enableWhen(controller.model.currentView.isNotEqualTo(ViewType.GAME_CREATION)) action { - logger.debug("New Game!") + logger.debug { "New Game!" } if(controller.model.currentView.get() == ViewType.GAME) { confirm( header = "Neues Spiel anfangen", diff --git a/src/main/kotlin/sc/gui/view/ControlView.kt b/src/main/kotlin/sc/gui/view/ControlView.kt index d60c9b8..e05e2c3 100644 --- a/src/main/kotlin/sc/gui/view/ControlView.kt +++ b/src/main/kotlin/sc/gui/view/ControlView.kt @@ -55,7 +55,7 @@ class ControlView: View() { } } val prev = button { - if(logger.isTraceEnabled) + if(logger.isTraceEnabled()) hoverProperty().listenImmediately { logger.trace { "$this: $padding on hover $it" } } @@ -86,7 +86,7 @@ class ControlView: View() { ) } button { - if(logger.isTraceEnabled) + if(logger.isTraceEnabled()) hoverProperty().listenImmediately { logger.trace { "$this: $padding on hover $it" } } diff --git a/src/main/kotlin/sc/gui/view/game/HuIBoard.kt b/src/main/kotlin/sc/gui/view/game/HuIBoard.kt index ba21f91..e1a5224 100644 --- a/src/main/kotlin/sc/gui/view/game/HuIBoard.kt +++ b/src/main/kotlin/sc/gui/view/game/HuIBoard.kt @@ -286,7 +286,7 @@ class HuIBoard: GameBoard() { putOnPosition( Button(carrotCostString(car.amount)).apply { fixHoverInsets() - if(logger.isTraceEnabled) + if(logger.isTraceEnabled()) hoverProperty().listenImmediately { logger.trace { "$this: $padding on hover $it" } } From f3e3061adefc207cc3e2d59c11f0d8d01b93504e Mon Sep 17 00:00:00 2001 From: xeruf Date: Thu, 19 Mar 2026 10:34:36 +0100 Subject: [PATCH 06/19] build: update backend and run CI on all supported versions --- .github/workflows/gradle.yml | 6 +++--- backend | 2 +- build.gradle.kts | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5d95dc4..84e4dbc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -7,7 +7,7 @@ jobs: matrix: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs os: [ubuntu-latest, windows-latest, macos-latest] - jdk: [17, 24] + jdk: [11, 17, 21] steps: - uses: actions/checkout@v4 with: @@ -55,7 +55,7 @@ jobs: - name: Upload jar as artifact uses: actions/upload-artifact@v4 with: - name: software-challenge-gui-${{ github.sha }}-${{ matrix.os }} + name: software-challenge-gui-${{ github.sha }}-j${{ matrix.jdk }}-${{ matrix.os }} path: build/*.jar release: needs: [build, build-arm] @@ -64,7 +64,7 @@ jobs: steps: - uses: actions/download-artifact@v4 # https://github.com/actions/download-artifact with: - pattern: software-challenge-gui-${{ github.sha }}-* + pattern: software-challenge-gui-${{ github.sha }}-j11-* path: build merge-multiple: true - name: Release ${{ github.ref }} diff --git a/backend b/backend index d5ae974..4727126 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit d5ae974203cd037a282a32e207ec9688daa50fd2 +Subproject commit 47271265795645fc6a815114276ec9e6c8c4ca78 diff --git a/build.gradle.kts b/build.gradle.kts index e4e09c0..26bb6b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,9 +3,10 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.util.Properties -val minJavaVersion = JavaVersion.VERSION_17 +val minJavaVersion = JavaVersion.VERSION_11 +val targetJavaVersion = JavaVersion.current() // minJavaVersion can be set for compatibility plugins { - val minJavaVersion = JavaVersion.VERSION_17 // Declared twice because plugins block has its own scope + val minJavaVersion = JavaVersion.VERSION_11 // Declared twice because plugins block has its own scope require(JavaVersion.current() >= minJavaVersion) { "Building requires at least JDK $minJavaVersion - please look into the README" } @@ -14,7 +15,7 @@ plugins { kotlin("jvm") version "2.3.0" id("idea") id("org.openjfx.javafxplugin") version "0.1.0" - id("com.gradleup.shadow") version "9.3.2" + id("com.gradleup.shadow") version "9.1.0" id("com.github.ben-manes.versions") version "0.53.0" id("se.patrikerdes.use-latest-versions") version "0.2.19" @@ -87,7 +88,7 @@ dependencies { tasks { compileJava { - options.release.set(minJavaVersion.majorVersion.toInt()) + options.release.set(targetJavaVersion.majorVersion.toInt()) } processResources { if(!debug) @@ -98,7 +99,7 @@ tasks { } withType { compilerOptions { - jvmTarget.set(JvmTarget.fromTarget(minJavaVersion.toString())) + jvmTarget.set(JvmTarget.fromTarget(targetJavaVersion.toString())) //freeCompilerArgs.addAll("-jvm-default=all") } } From 2a0ffbfa7377cbfb20b008ad9dc550cd3c91c289 Mon Sep 17 00:00:00 2001 From: xeruf Date: Thu, 2 Apr 2026 12:11:38 +0200 Subject: [PATCH 07/19] build: update backend --- backend | 2 +- src/main/kotlin/sc/gui/view/game/PenguinsBoard.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend b/backend index 4727126..d41ec23 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 47271265795645fc6a815114276ec9e6c8c4ca78 +Subproject commit d41ec23b91a56fc94232d467e83ec3e323c1b440 diff --git a/src/main/kotlin/sc/gui/view/game/PenguinsBoard.kt b/src/main/kotlin/sc/gui/view/game/PenguinsBoard.kt index 50571cd..ef117e8 100644 --- a/src/main/kotlin/sc/gui/view/game/PenguinsBoard.kt +++ b/src/main/kotlin/sc/gui/view/game/PenguinsBoard.kt @@ -53,7 +53,7 @@ class PenguinBoard: View() { size.bind(Bindings.min(widthProperty(), heightProperty().multiply(1.6))) anchorpane { this.paddingAll = AppStyle.spacing - val stateListener = ChangeListener { _, oldState, state -> + val stateListener = ChangeListener { _, oldState: GameState?, state -> clearTargetHighlights() if(state == null) { //children.remove(BOARD_SIZE.toDouble().pow(2).toInt(), children.size) From 98c7090b851302424f3a24c6296aa506a357272b Mon Sep 17 00:00:00 2001 From: xeruf Date: Mon, 20 Apr 2026 08:56:54 +0200 Subject: [PATCH 08/19] build(gradle): update backend and distribution naming --- backend | 2 +- build.gradle.kts | 2 +- settings.gradle.kts | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend b/backend index d41ec23..04b1ecf 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit d41ec23b91a56fc94232d467e83ec3e323c1b440 +Subproject commit 04b1ecff13cc8b9eddda7a365b3f2ce72f77ab01 diff --git a/build.gradle.kts b/build.gradle.kts index 26bb6b2..d74fddb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ val versionFromBackend by lazy { arrayOf("year", "minor", "patch").map { versions["socha.version.$it"].toString().toInt() }.joinToString(".") + suffix } -group = "sc.gui" +group = "software-challenge" version = try { Runtime.getRuntime().exec(arrayOf("git", "describe", "--tags")) .inputStream.reader().readText().trim().ifEmpty { null } diff --git a/settings.gradle.kts b/settings.gradle.kts index 0ebfb25..5a5913a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,2 @@ -rootProject.name = "software-challenge-gui" - includeBuild("backend/gradle/custom-tasks") -includeBuild("backend") \ No newline at end of file +includeBuild("backend") From d6d5d2e0a8c22b7cd181c446d4f9d59121cfadc2 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 27 May 2026 21:20:59 +0200 Subject: [PATCH 09/19] chore(vier-gewinnt): Setup GUI for connect 4 --- .gitmodules | 1 + backend | 2 +- build.gradle.kts | 1 + src/main/kotlin/sc/gui/AppStyle.kt | 26 ++++++++++++++++++- .../META-INF/services/sc.gui.view.GameBoard | 2 +- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9bc842f..ae0d755 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,7 @@ path = backend url = https://github.com/software-challenge/backend shallow = true + branch = plugin/4-gewinnt [submodule ".idea"] path = .idea url = https://github.com/software-challenge/idea-config diff --git a/backend b/backend index 04b1ecf..5f739a2 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 04b1ecff13cc8b9eddda7a365b3f2ce72f77ab01 +Subproject commit 5f739a2425252f94f5df6921f06c4ae4dab93f51 diff --git a/build.gradle.kts b/build.gradle.kts index d74fddb..b3a800a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -79,6 +79,7 @@ dependencies { implementation("software-challenge", "plugin2024") implementation("software-challenge", "plugin2025") implementation("software-challenge", "plugin2026") + implementation("software-challenge", "plugin2098") if(debug) { // hold Ctrl to view component hierarchy and bounds diff --git a/src/main/kotlin/sc/gui/AppStyle.kt b/src/main/kotlin/sc/gui/AppStyle.kt index 7371528..5834724 100644 --- a/src/main/kotlin/sc/gui/AppStyle.kt +++ b/src/main/kotlin/sc/gui/AppStyle.kt @@ -157,7 +157,31 @@ class AppStyle: Stylesheet() { prefWidth = 100.percent } - piranhasStyles() + connect4Styles() + } + + fun connect4Styles() { + background { + opacity = 0.7 + backgroundColor += c("#88DAF7") + backgroundImage += resources.url("/piranhas/water_b.png").toURI() + backgroundRepeat += BackgroundRepeat.REPEAT to BackgroundRepeat.REPEAT + } + + (1..3).forEach { size -> + Team.entries.forEach { team -> + ".${team}_${size}" { + image = resources.url("/piranhas/${team.color}_${(96 + size).toChar()}.png") + .toURI() + } + } + } + + ".squid" { image = resources.url("/piranhas/squid.png").toURI() } + ".grid" { + backgroundImage += resources.url("/piranhas/grid-crop.png").toURI() + backgroundSize += BackgroundSize(1.0, 1.0, true, true, false, false) + } } fun piranhasStyles() { diff --git a/src/main/resources/META-INF/services/sc.gui.view.GameBoard b/src/main/resources/META-INF/services/sc.gui.view.GameBoard index aaec2a4..bc28cf4 100644 --- a/src/main/resources/META-INF/services/sc.gui.view.GameBoard +++ b/src/main/resources/META-INF/services/sc.gui.view.GameBoard @@ -1 +1 @@ -sc.gui.view.game.PiranhasBoard +sc.gui.view.game.Connect4Board From c9b9819dfccb288981dca252b69dc2c9ddef1b9d Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 27 May 2026 22:19:37 +0200 Subject: [PATCH 10/19] resources(vier-gewinnt): Add simple graphics The visualization does not work at the moment --- src/main/kotlin/sc/gui/AppStyle.kt | 9 +- .../kotlin/sc/gui/view/game/Connect4Board.kt | 162 ++++++++++++++++++ src/main/resources/connect4/board.png | Bin 0 -> 24201 bytes src/main/resources/connect4/cell-debug.png | Bin 0 -> 155 bytes src/main/resources/connect4/cell.png | Bin 0 -> 746 bytes src/main/resources/connect4/chip-gelb.png | Bin 0 -> 1512 bytes src/main/resources/connect4/chip-rot.png | Bin 0 -> 1499 bytes 7 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/sc/gui/view/game/Connect4Board.kt create mode 100644 src/main/resources/connect4/board.png create mode 100644 src/main/resources/connect4/cell-debug.png create mode 100644 src/main/resources/connect4/cell.png create mode 100644 src/main/resources/connect4/chip-gelb.png create mode 100644 src/main/resources/connect4/chip-rot.png diff --git a/src/main/kotlin/sc/gui/AppStyle.kt b/src/main/kotlin/sc/gui/AppStyle.kt index 5834724..3a141de 100644 --- a/src/main/kotlin/sc/gui/AppStyle.kt +++ b/src/main/kotlin/sc/gui/AppStyle.kt @@ -11,6 +11,7 @@ import javafx.scene.text.FontWeight import javafx.scene.text.TextAlignment import sc.api.plugins.Team import sc.gui.model.AppModel +import sc.plugin2098.util.Connect4Constants import tornadofx.* class AppStyle: Stylesheet() { @@ -177,10 +178,12 @@ class AppStyle: Stylesheet() { } } - ".squid" { image = resources.url("/piranhas/squid.png").toURI() } + ".chip-r" { image = resources.url("/connect4/chip-rot.png").toURI() } + ".chip-y" { image = resources.url("/connect4/chip-gelb.png").toURI() } + ".cell" { image = resources.url("/connect4/cell-debug.png").toURI() } ".grid" { - backgroundImage += resources.url("/piranhas/grid-crop.png").toURI() - backgroundSize += BackgroundSize(1.0, 1.0, true, true, false, false) + backgroundImage += resources.url("/connect4/board.png").toURI() + backgroundSize += BackgroundSize(1.0, 0.1, true, true, false, false) } } diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt new file mode 100644 index 0000000..a5df6a6 --- /dev/null +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -0,0 +1,162 @@ +package sc.gui.view.game + +import javafx.application.Platform +import javafx.geometry.Insets +import javafx.geometry.Point2D +import javafx.geometry.Pos +import javafx.scene.Node +import javafx.scene.effect.ColorAdjust +import javafx.scene.effect.Glow +import javafx.scene.input.KeyEvent +import javafx.scene.layout.GridPane +import sc.api.plugins.Coordinates +import sc.gui.util.listenImmediately +import sc.gui.view.GameBoard +import sc.gui.view.PieceImage +import sc.gui.view.transitionDuration +import sc.plugin2098.FieldState +import sc.plugin2098.GameState +import sc.plugin2098.util.GameRuleLogic +import sc.plugin2098.util.Connect4Constants +import tornadofx.* + +class Connect4Board: GameBoard() { + + private val gridSize + get() = squareSize.div(Connect4Constants.BOARD_WIDTH) // "Length of the smaller side of the window." + + val grid: GridPane = GridPane().addClass("grid").apply { + squareSize.listenImmediately { size -> + padding = Insets( + size.toDouble() / 80, + size.toDouble() / 80, + size.toDouble() / 300, + size.toDouble() / 200, + ) + } + } + + override val root = hbox { + this.alignment = Pos.CENTER + vbox { + this.alignment = Pos.CENTER + add(grid) + } + } + + var selected: Node? = null + val hovers = ArrayList() + + fun clearHovers() { + logger.trace { "Clearing hovers: $hovers" } + grid.children.removeAll(hovers) + hovers.clear() + } + + fun addToGrid(child: Node, coordinates: Coordinates) { + grid.add(child, coordinates.x, Connect4Constants.BOARD_WIDTH - 1 - coordinates.y) + } + + override fun onNewState(oldState: GameState?, state: GameState?) { + selected = grid + logger.debug { "New State: $state" } + grid.children.clear() + hovers.clear() + selected = null + + // this ensures proper sizing of the board + (0 until Connect4Constants.BOARD_WIDTH).forEach { y -> + + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) + } + + (0 until Connect4Constants.BOARD_HEIGHT).forEach { y -> + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) + + } +// +// state?.let { state -> +// val move = state.lastMove?.let { move -> +// if(oldState?.turn?.minus(state.turn) == -1) { +// move.from to GameRuleLogic.targetCoordinates(oldState.board, move) +// } else { +// null +// } +// } +// state.board.forEach { (pos: Coordinates, field: FieldState) -> +// val piece = PieceImage( +// gridSize, +// field.team?.let { team -> "${team}_${field.size}" } ?: field.name.lowercase()) +// +// addToGrid(piece, pos) +// if(pos == move?.second) { +// val offset = move.first - move.second +// logger.debug { "Animating piece $piece (${piece.translateX}|${piece.translateY}) along $move from $offset" } +// piece.effect = Glow(0.2) +// piece.translateX = offset.dx * gridSize.value +// piece.translateY = - offset.dy * gridSize.value +// piece.move(transitionDuration, Point2D.ZERO) +// } +// +// if(field.team == null) +// return@forEach +// piece.hoverProperty().addListener { _, _, hover -> +// if(selected == null) { +// if(hover) { +// Platform.runLater { +// addHovers(state, pos, field) +// } +// } else { +// if(field.team != state.currentTeam || !awaitingHumanMove.value) +// clearHovers() +// } +// } +// } +// piece.onLeftClick { +// if(field.team == state.currentTeam && awaitingHumanMove.value) { +// logger.debug { "Clicked own fish on $pos" } +// selected?.effect = null +// if(selected == piece) { +// clearHovers() +// selected = null +// return@onLeftClick +// } +// selected = piece +// piece.effect = Glow(0.6) +// addHovers(state, pos, field) +// } +// } +// } +// } + } + + fun addHovers(state: GameState, pos: Coordinates, field: FieldState) { +// logger.trace { "Clearing hovers and adding for $pos in turn ${state.turn}" } +// clearHovers() +// +// val board = state.board +// GameRuleLogic.possibleMovesFor(board, pos).forEach { move -> +// val target = GameRuleLogic.targetCoordinates(board, move) +// val hover = PieceImage(gridSize, "${field.team}_${field.size}") +// +// val current = field.team == state.currentTeam +// hover.effect = ColorAdjust().apply { +// saturation = if(current && awaitingHumanMove.value) -0.4 else -0.9 +// } +// if(current) +// hover.onLeftClick { sendHumanMove(move) } +// +// hovers.add(hover) +// addToGrid(hover, target) +// } + } + + override fun handleKeyPress(state: GameState, keyEvent: KeyEvent): Boolean { + return false + } + + override fun renderHumanControls(state: GameState) { + // not needed for piranhas, handled abovene + } + +} \ No newline at end of file diff --git a/src/main/resources/connect4/board.png b/src/main/resources/connect4/board.png new file mode 100644 index 0000000000000000000000000000000000000000..58247b584d47b82498f1b19aa2144f57bca2bc0a GIT binary patch literal 24201 zcmeI43s6&68pqRCtPj++t_1~TsumqZ9-^S-wJH`Z2v$LKL=eNnU88_lo?+P$7b*go z76k!?u?%c0F3(vY3&yfMvIrq-Z7UR%M+CI;is30p_9ldzdv4CXaW=a%?d+N1k(_&S zAGzm#-}n3e=R21Vx;QRbw0aSP!C10?pTi*rP7xSLVX ztp6N)GcRD*fn5wnF=z3#j~4cR{y}HAy$mBM_Hw((F9ZAc_Ju<(hoy|3a;?aN@CnAz{J%Prh%pxr68|L+Cszx9x0U)YjYmb}(@-)%`@%k|q`EA6_JF#pc(9i+$}< z+O}ploJ|)#Eo%xmf4tX%yG4@To;ta0skivVe5=9{%*mSr5a_7gUQEMw6G-fS0 zH&U|Uq8_n{;gVQ%CiV5Y{tQ3qf=d5Z`+pQ18{-g~jE>pg=dB9mlmx~-&gswe8Q(bN z-(pK_IytHr%JUnd`{O%se2nMQNB<|vl2a{+R1c;#@z_~j4UD@ARm2Oo>iZW|w*u*`@G zW9td-TJw^6pBQx1hln#wMci;f8a7ya8^cY!Bz`tJg_B#et~_F|64ssJHGyM#ReM!; zrS>^5eCVgKuS14Dsat%0ANyL(4?M!!EZ|c)} zH)XP_Gxh=Xwv$`4b=YPZ(9g-X={w7JzUWDjg-5ksXz!-*nr2U}z?Ss<&L!3%E3140 z3NBhr^G}kS`kUN`<8E>lpPOM8U9z!8)oo;f2y|9nF9q!1?O9rBa~0C{OB=>cmPJ+ zu`)`NnHr6Zh*b9u!D>Nv6{CHGV6{LKC9`&21d|B0sfLM~+G1Km{|4Sb=5prz3d}FX zi{&l{wn^@?ph54fvx7NT&}(K7?}z(fyqjHD3XQ9lbz{pNzbKQ&@n7Z4#RpW~;L*lX z58>n{MNpy=wv%F)*vvNwaR>oZVqf=mZpBrFV8~hw>S`+FJ{FiuxPurjSIQ}*)vSoSQO|a=z%X>A~ z?8<6`ayeVfJ)bk3v^_{oey;PZB3oBfCwYxI1m&dJE^mJZHf0}>cs>hwXruG0b(`?d zf3y^Y+ya}HhYCt!E}batH}rXAJmuK3y>SL?;_>L}HVg8^Uzbjih9ZKdnR4(kSg_60FS-%{GlQ|CS3y<7WN8i|Bre}EhR*=UbkIxPKm{hP%MdmoH zgZT&LRpMC+Dl*4mJ%KLcA^%>XsS@Q4;ipoOIS%V!CPsO`mH8Tw$6>Y^O!vTs7s%s) zLr`El$m1~gLgaDc6FZlRYKVsneNx7X#N$K{slawWB9BA-?P0t7D%h^ljmYD8ETF`8 z*NOEi*ba76A6Z~~Bbz4zvJ-$D5*_*2%(6OUlLF|8K%UY{2J(Bl&17mPcOnA0Y7}Xw zD^S3?>NYvizng=BJjJ$I+SfF+wV%Hk#`buWJZu=NzS4C!df)KYDYK_PQr&_~YBP0O|*v;YU(;C`hFg;5)^p@jl zy#YK?j60vtbcLiGGQLTkXf}}xQBY?$1$s?bPF#Jmdxc$tB6Uu+H_CiH?)Y6NH`1i_ z<=rIikZ6M_65ItPhyN_ZQKNHUag{*_Ec_y>ChAxr^$c990YXCxGRI;4X9_Y`!a5b1 zV{X=71+=xV*Bd&Ss@Cd zSF^o}Xk-(XGp$fg2kFzxCft9bbxBA@mn-W?M#nQA@)-+AMn^I_G<8R*Ngx@0R(?k^ zIwq_m86C|c_{C150D)X}u8QV_tuf@ELNYp%(dE{kb~U8N&jmGeC7I*OI!QAp zfZ&|64r=B&nJYOH)XZ@*S1RlD%!(=NB+VQrbEUElYUU71Mlw24Sw}KDHmx0^B_bJ} zEUhCMeQwS>lF`B0??^_MX&OjISF3Y777 zlF_kJC?uo5ABJRftl|;L=&(8ONJa;09FU9-pCo{)(MiSW@Tw40jSgFofTrXKRincv z!O@hNqiS?ijjpJXMb+r28l7St091^h5Qb!Q*+Ltr8vO;TMxR@23RR;61?osfC)WL< zYIIbMj;hgt73@$oI=+Y;szxWRC4;KbHLQR{)#%uQp{N=iEm-$|yj~xY(cfg{OH_@n z>C&sH8lAL;uex$!REp E8<5SQy#N3J literal 0 HcmV?d00001 diff --git a/src/main/resources/connect4/cell-debug.png b/src/main/resources/connect4/cell-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..5415b4e8e0f7886abf3f5cc5f2f42a4c4b2b9de2 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^Js`}&3?yUT#qa_t#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6anM zpo(};7srr_xVPsV^PU*+upD^v-+phyvBEU1O}ddPjSTCGH_wib??3+dUNcHCl2kJdKSFr3z;mcXP6YuPLRI79S zT&3*Uy~g*B6@Q#DUGk32lSDT2{)*{`uiCBOwBz>EK(X@bJtrR*wX0Xgy!)Wie|mrA z#FM!*&p);5d0zB)^2?le^#*g!{w+`Y{PUpn;-7yW^j@r~`*1jgwf=_Wng{&W&7yx2 z(>FBP*BFN%);Bwr_2cNV2;TiSBvwBZmrgeQ^XO=Z=>H6!(2xDc0%*k$4>tXil)R=b zKBwpEhwk2uQgs^*!w&23o4FdveQ$WeKD5iLq4?rlj@fs6iF42XsbAh1)BZ7L-RT;& z?e(9P`cKQwds4Xb@MIgk?-i4e-qq^|hSc*=zHnf;9X#tSmsaq3#`KRdHji^nz5_$| z^j7VA?nSR>@fOBNCAy20L*vh-AbF1O>zEmd*R_5FW9rzq$Zu;mRL|@EezT^1Zu!mX z$y>{B>^$%)@|*6ay0%!xIk5OUwlLeQrRdF^>p&YG#Y(lO+Rc~WGo#8zuK&3F2YFF( WUDb^A6@I|<$KdJe=d#Wzp$P!%FKA5w literal 0 HcmV?d00001 diff --git a/src/main/resources/connect4/chip-gelb.png b/src/main/resources/connect4/chip-gelb.png new file mode 100644 index 0000000000000000000000000000000000000000..b54cee4b8cf3af1e2ba2a7798237ed9f95eb60ba GIT binary patch literal 1512 zcma)+dpOez9L6Wc+~!g)XKuMP%9O-3h8DSvrBS&|EmNt8tYHzECUl`Nmr+r!ErzBu zPWb7OA_p#=xVt=ORBp z$Av`sU0!-)-wcfm$t1?U{nV}*-gvTzsoUqvzL#Wc+vQchVW$DRF|!b4lae|lHsD6- zMb|2|bXIGGrurJkds&1N9DDPQoeKyC+uaabVpR#YR-`|^W_Yrkv3PB@bF`LAM zT>HfN0mL4YIfjfG2f~%+2R$Fyuhar|H@~Ey9geCf;Jq>D3jMe8T+HI*=TmC$^WB8h zSbz6iCo$d0LR|`O)7kjPr*AJfLHk|W5hIa1KTil7s!Yws3w5h^kp?<1#`H*6M|=e3y0;ntNhtfro|W>K-<#s0TLdPoR*%MDjNB%Xy^|*w)oT4 zbJ?T{V%2*Iu0+G5NV%1bgUXWegQ1>=)Jp`14x`%~6t!9cTj81(KBUMh6r{R5@eET& zK)}Sa42!H?h^oDi_Afx*S_yJE*<^2K!akmnO4y<4Z(~ff)da=zh1yZu*Ck$M$+T%o z{4H7*c^U2*G@G-Iaf4nq+H9W`g2E}QY=R?L%7L4NB>0R~^1rTzDkcx6jXME?A?+Vg zgYN{FgMBD-(h3`qH^p)iY`koh`)edlN6CkFw%J%7e+iCZ6*Npw`L;fFhPvPgaUb{@ zHG(+($wR>25-0jp`N}Y@l9=)Ey#b7-q0O8$v|JKdm-mqpu|y8y6;2eV#N5=Odba98 z!W;u?G-cF9fsR<>H8BL(VVkduJ#mS> zR$YoYG>JqajtBVwCEoe7PZl`HTeMxkOGk%(-E?nm;Ql1t*~qa!kXdW`I4sPs&zHXh z3nq6(i4*3oxuHK+IP;_)bmvNjJ6bE94zmwKn8_o~WuI-6yVZE2OnfBEZbB956_@|e zLFdgm+htI9)0ab&0gLT@0wuZDeUIx)S}#^g?Cc`X%ZM}sSK%L~Sby~4n>v%U?td4m zMl=;mwZ9T_HC>irA+a>i8UItpPqdYeQc&!>{li$S*rDTr9fqR)F6J4L^rl8Vz1#6R zi{B3#a834_wfO-%f)}Vq7VCJ(96oCxr^4G9vp64^jx`KrgHcrd-U&h*9Dr}0?Y@%2 zs0+&Y2bW=iC)JU=QC2FrI4u=L#2q25WNrN-4E_n>)b;HlFI^k|$lOvjPWh|4 zx|Xg-o93S6CQ9?ShF3&U{l0AaY+Z8gj^hvNMuqawoq@LLRl@}40>{U( zS{?2w6o=e+xx`xa%0Qzn9ylZ_K~)eu4}FSk&mE57yjn?RDI>7-yHAn=>me+=MqK^& z+%x(>iyD{d+Tp#Tmb8oYu&ZeOB}w~0#&yF~4_v{=f@-hm@O_)2t;M+0Qm?2>+us@F MbPQ)#c9cl_8&c@ZMgRZ+ literal 0 HcmV?d00001 diff --git a/src/main/resources/connect4/chip-rot.png b/src/main/resources/connect4/chip-rot.png new file mode 100644 index 0000000000000000000000000000000000000000..07228353a491f23ee7b35663be846fd71f97f4e1 GIT binary patch literal 1499 zcma)+c{~#e0LSOC+#`gt^ju?=E7#mPCUPtrHDw;O&KjQTJnwQ^zRz6qeKGW& z>u`Sf80b0F0Bv`QuPqywJ(A|SOkHkBzH0zMch-(s?+|LB4|xqUsxK&$lbmH))V$3@ z-m~{O+Tit3N|7=&5_m(c{aQxyJgFaXR;&wy;ORO$Cf&;t*bkMs40F7i$HU?u&GlSG z<@$-$ak*NNo}YBkG&Yex(xAl3^c!4*tg-`j>rC}$xmo~?7H(Z6kaMxVvlw^tNZ2Df z*S3}K83)TkT%1vcTLn196jKdr%-|MjF)|$A1dHZP80opWjounsJ9RuldQhPZXriCr zn;y}6(rA{=EKVKf;@J}?*XtpMc5Zvd5nUDHH)l0-j|dRtSDKXwEif>Lc= z(J0y6f2}1y__R@cB90HAcxM$mp3MFrBUPy;@NIJ+ty_gQ-bx2=?*(7u=WIJ6<-S}l zJ!5P2yC{}eo97BYtc%+>@VI!W$v|YxHusI@!CP9aI?`5H@dUr06IoggBAMBCr^MhK z-h9;al#6VV!6rob8GH4IcD|Xx$rz}$444!Q1Ez$}E}hkiBG^6hPgQ<6iHQky!%({8 zy>*l4MwMRNeB~BmM>$9$Uy&CqX%OU#D}%P_No>xog(oY++FqKOs)?lgPK=IcfzvQT z^;M>XIS8mm{)J9e-_RR@DP09i{U0nG+Q$?QJvNO0H8*82=T^X!gUeO}vWA>M#ejbp zumX2j{Dq)!nGA1I03{7SA`|U^bZ*76gPgG2aP9SiKkBtJCh?X*(6=6gd*sO#gq-Ol z5TSP7sOcWiK7pLj*~U|CR#5*@xMR1{r>7%MO>Fax!W6DeG%e_?_xjg68^>i%Uyl0j zasLjIu?;V@o`7yRMtyGrMQ#l1ZS5GVlVPY8v%Y>rR^DvQB@)@`YcLYpI>HL{KrKt8 zB=bX9{pT2!(^XF~xBG@avv}<}ft==LS&e@+u3zlt(QH!g^MfzHvu;O^Fk-Q zsPL_ZiY4%ff6hq9SbG9u)c5sgdDxxJdsw|RTfJK86qK}%p<<(VWjz?>5g)6imP-uk zQdh(modxtN;JlI5nwV`iejNz!JRetR@|eyoQc@}B#c!YMe8(CgV95hu2ztN1b+qY- z(d}E%O*m<%rOFXcNMQnt#i(l9#Ow>P9Smp=1m@{mMDwF5E9QU0dMTE>iZ(@6d@EhC zRR8L_2+PqqZHcL-p>la`0;jWO7=os6##*hX*~0S!3}dTA7sHsehF;}7uTQ+%09MVs ydZ`k)pcO%^LP4w2mVF?!tg*KFrPe9LhMY4a^A*Ny Date: Thu, 4 Jun 2026 00:24:55 +0200 Subject: [PATCH 11/19] feat: Display pieces and display possible moves --- src/main/kotlin/sc/gui/AppStyle.kt | 6 +- src/main/kotlin/sc/gui/view/PieceImage.kt | 1 - .../kotlin/sc/gui/view/game/Connect4Board.kt | 104 ++++++------------ 3 files changed, 37 insertions(+), 74 deletions(-) diff --git a/src/main/kotlin/sc/gui/AppStyle.kt b/src/main/kotlin/sc/gui/AppStyle.kt index 3a141de..109fef1 100644 --- a/src/main/kotlin/sc/gui/AppStyle.kt +++ b/src/main/kotlin/sc/gui/AppStyle.kt @@ -178,12 +178,12 @@ class AppStyle: Stylesheet() { } } - ".chip-r" { image = resources.url("/connect4/chip-rot.png").toURI() } - ".chip-y" { image = resources.url("/connect4/chip-gelb.png").toURI() } + ".one-chip" { image = resources.url("/connect4/chip-rot.png").toURI() } + ".two-chip" { image = resources.url("/connect4/chip-gelb.png").toURI() } ".cell" { image = resources.url("/connect4/cell-debug.png").toURI() } ".grid" { backgroundImage += resources.url("/connect4/board.png").toURI() - backgroundSize += BackgroundSize(1.0, 0.1, true, true, false, false) + backgroundSize += BackgroundSize(1.0, 1.0, true, true, false, false) } } diff --git a/src/main/kotlin/sc/gui/view/PieceImage.kt b/src/main/kotlin/sc/gui/view/PieceImage.kt index 3cc18a3..37720f0 100644 --- a/src/main/kotlin/sc/gui/view/PieceImage.kt +++ b/src/main/kotlin/sc/gui/view/PieceImage.kt @@ -84,7 +84,6 @@ class PieceImage(private val sizeProperty: ObservableDoubleValue, val content: S } fun addChild(graphic: String, index: Int? = null) { - //logger.trace { "$this: Adding $graphic" } children.add(index ?: children.size, ResizableImageView(sizeProperty).apply { addClass(graphic) if(graphic == "penguin") diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index a5df6a6..760c4e5 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -66,16 +66,18 @@ class Connect4Board: GameBoard() { // this ensures proper sizing of the board (0 until Connect4Constants.BOARD_WIDTH).forEach { y -> - - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) + //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, y, 0) } (0 until Connect4Constants.BOARD_HEIGHT).forEach { y -> - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) - + //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, 0, y) } -// -// state?.let { state -> + //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, Connect4Constants.BOARD_WIDTH - 1, Connect4Constants.BOARD_HEIGHT - 1) + + + state?.let { state -> // val move = state.lastMove?.let { move -> // if(oldState?.turn?.minus(state.turn) == -1) { // move.from to GameRuleLogic.targetCoordinates(oldState.board, move) @@ -83,72 +85,23 @@ class Connect4Board: GameBoard() { // null // } // } -// state.board.forEach { (pos: Coordinates, field: FieldState) -> + state.board.forEach { (pos: Coordinates, field: FieldState) -> // val piece = PieceImage( // gridSize, // field.team?.let { team -> "${team}_${field.size}" } ?: field.name.lowercase()) -// -// addToGrid(piece, pos) -// if(pos == move?.second) { -// val offset = move.first - move.second -// logger.debug { "Animating piece $piece (${piece.translateX}|${piece.translateY}) along $move from $offset" } -// piece.effect = Glow(0.2) -// piece.translateX = offset.dx * gridSize.value -// piece.translateY = - offset.dy * gridSize.value -// piece.move(transitionDuration, Point2D.ZERO) -// } -// -// if(field.team == null) -// return@forEach -// piece.hoverProperty().addListener { _, _, hover -> -// if(selected == null) { -// if(hover) { -// Platform.runLater { -// addHovers(state, pos, field) -// } -// } else { -// if(field.team != state.currentTeam || !awaitingHumanMove.value) -// clearHovers() -// } -// } -// } -// piece.onLeftClick { -// if(field.team == state.currentTeam && awaitingHumanMove.value) { -// logger.debug { "Clicked own fish on $pos" } -// selected?.effect = null -// if(selected == piece) { -// clearHovers() -// selected = null -// return@onLeftClick -// } -// selected = piece -// piece.effect = Glow(0.6) -// addHovers(state, pos, field) -// } -// } -// } -// } - } - - fun addHovers(state: GameState, pos: Coordinates, field: FieldState) { -// logger.trace { "Clearing hovers and adding for $pos in turn ${state.turn}" } -// clearHovers() -// -// val board = state.board -// GameRuleLogic.possibleMovesFor(board, pos).forEach { move -> -// val target = GameRuleLogic.targetCoordinates(board, move) -// val hover = PieceImage(gridSize, "${field.team}_${field.size}") -// -// val current = field.team == state.currentTeam -// hover.effect = ColorAdjust().apply { -// saturation = if(current && awaitingHumanMove.value) -0.4 else -0.9 -// } -// if(current) -// hover.onLeftClick { sendHumanMove(move) } -// -// hovers.add(hover) -// addToGrid(hover, target) -// } + + println(field.team ?: field.name) + + if(field.team == null) { + return@forEach + } + + println("${field.team!!.name}_chip".lowercase()) + + val piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip".lowercase() }) + addToGrid(piece, pos) + } + } } override fun handleKeyPress(state: GameState, keyEvent: KeyEvent): Boolean { @@ -156,7 +109,18 @@ class Connect4Board: GameBoard() { } override fun renderHumanControls(state: GameState) { - // not needed for piranhas, handled abovene + state.getSensibleMoves().forEach { move -> + + val piece = PieceImage(gridSize, "${state.currentTeam}-chip".lowercase()) + + piece.opacity = 0.7 + + piece.onLeftClick { + sendHumanMove(move) + } + + addToGrid(piece, move.position) + } } } \ No newline at end of file From 83c1daa1cf6f0f0200c940f330584278872ee730 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 4 Jun 2026 00:31:18 +0200 Subject: [PATCH 12/19] feat: Keyboard inputs --- .../kotlin/sc/gui/view/game/Connect4Board.kt | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 760c4e5..64122f7 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -78,26 +78,11 @@ class Connect4Board: GameBoard() { state?.let { state -> -// val move = state.lastMove?.let { move -> -// if(oldState?.turn?.minus(state.turn) == -1) { -// move.from to GameRuleLogic.targetCoordinates(oldState.board, move) -// } else { -// null -// } -// } state.board.forEach { (pos: Coordinates, field: FieldState) -> -// val piece = PieceImage( -// gridSize, -// field.team?.let { team -> "${team}_${field.size}" } ?: field.name.lowercase()) - - println(field.team ?: field.name) - if(field.team == null) { return@forEach } - println("${field.team!!.name}_chip".lowercase()) - val piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip".lowercase() }) addToGrid(piece, pos) } @@ -105,6 +90,18 @@ class Connect4Board: GameBoard() { } override fun handleKeyPress(state: GameState, keyEvent: KeyEvent): Boolean { + println(keyEvent.text) + + var x = keyEvent.text.toIntOrNull() ?: return false + x -= 1 + + state.getSensibleMoves().forEach { move -> + if(move.position.x == x) { + sendHumanMove(move) + return true + } + } + return false } From 654cd9510eb38c0e565a3a7b07f59541879d8836 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 4 Jun 2026 00:32:24 +0200 Subject: [PATCH 13/19] chore: Remove println --- src/main/kotlin/sc/gui/view/game/Connect4Board.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 64122f7..6c7faca 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -90,7 +90,6 @@ class Connect4Board: GameBoard() { } override fun handleKeyPress(state: GameState, keyEvent: KeyEvent): Boolean { - println(keyEvent.text) var x = keyEvent.text.toIntOrNull() ?: return false x -= 1 From 1dac3274783ae80fb39845732d83074dc1519b9c Mon Sep 17 00:00:00 2001 From: NichtNil5 Date: Thu, 4 Jun 2026 23:23:02 +0200 Subject: [PATCH 14/19] fix(vier-gewinnt): Fixed wrong Constant in addToGrid Changed addToGrid to use BOARD_HEIGHT instead of BOARD_WIDTH to calculate the rowIndex. --- src/main/kotlin/sc/gui/view/game/Connect4Board.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 6c7faca..e049916 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -1,6 +1,7 @@ package sc.gui.view.game import javafx.application.Platform +import javafx.beans.binding.DoubleBinding import javafx.geometry.Insets import javafx.geometry.Point2D import javafx.geometry.Pos @@ -37,7 +38,7 @@ class Connect4Board: GameBoard() { } override val root = hbox { - this.alignment = Pos.CENTER + this.alignment = Pos.BOTTOM_CENTER vbox { this.alignment = Pos.CENTER add(grid) @@ -54,7 +55,7 @@ class Connect4Board: GameBoard() { } fun addToGrid(child: Node, coordinates: Coordinates) { - grid.add(child, coordinates.x, Connect4Constants.BOARD_WIDTH - 1 - coordinates.y) + grid.add(child, coordinates.x, Connect4Constants.BOARD_HEIGHT - 1 - coordinates.y) } override fun onNewState(oldState: GameState?, state: GameState?) { @@ -63,7 +64,7 @@ class Connect4Board: GameBoard() { grid.children.clear() hovers.clear() selected = null - + // this ensures proper sizing of the board (0 until Connect4Constants.BOARD_WIDTH).forEach { y -> //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) @@ -82,7 +83,6 @@ class Connect4Board: GameBoard() { if(field.team == null) { return@forEach } - val piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip".lowercase() }) addToGrid(piece, pos) } From 6d2b2c97690a05152be8c594c53026f009762da7 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 4 Jun 2026 23:30:31 +0200 Subject: [PATCH 15/19] refactor: Update chip images to fit right size --- .../kotlin/sc/gui/view/game/Connect4Board.kt | 6 ------ src/main/resources/connect4/chip-gelb.png | Bin 1512 -> 747 bytes src/main/resources/connect4/chip-rot.png | Bin 1499 -> 747 bytes 3 files changed, 6 deletions(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 6c7faca..2011972 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -47,12 +47,6 @@ class Connect4Board: GameBoard() { var selected: Node? = null val hovers = ArrayList() - fun clearHovers() { - logger.trace { "Clearing hovers: $hovers" } - grid.children.removeAll(hovers) - hovers.clear() - } - fun addToGrid(child: Node, coordinates: Coordinates) { grid.add(child, coordinates.x, Connect4Constants.BOARD_WIDTH - 1 - coordinates.y) } diff --git a/src/main/resources/connect4/chip-gelb.png b/src/main/resources/connect4/chip-gelb.png index b54cee4b8cf3af1e2ba2a7798237ed9f95eb60ba..59421bc896fbf31669b1e316912451e026f5248d 100644 GIT binary patch delta 729 zcmV;~0w(?F3+n|TiBL{Q4GJ0x0000DNk~Le0001t0001t2m=5B0GS@K43Qxqe+E!Y zR7L;T00000_$DTs00002bW%=J{{ZE;FiHRb010qNS#tmY4#EHc4#EKyC`y0;00Ll1 zL_t(|UhR%alEg3!!+QSz>8bP@A*OfBIS8jHmPkDQZp4E>b`mm*kWqw;B4iXJGLIzO zYM}F$288={beyqR!Mif8Cv42af9nEm`z$NNge)!FEUJSk85(xkQVVl5)vK&%fmw>m zMaH+nyfjs7jP8M%DJqs2+Y58AlvkM950ftxH<&mAvq@=zd1Ejg6yBLO3iB=bXJ(DV z21E9hNi$%_ncO3D=D?N{nK!1)f;~U<6Eo()rZ4h^2{U2W2lc@Fxv=exfB4Vq*|6_L z`ZsUq!^S7czr0)lI}g%79bo4{>L>5kz}B=3uU5g{+sp7~9c&Jn4{W~5@M0yGZqBZ- zoU9N$SBe> z-0~u16d|Js8AZq_LPilXe~OS%gp49&6d|Lyzsx3^vp~q42^ocu*5G0@X2YHznWsfIY&ns8TVlhGGug)lHf%8DU#HtJ-%@yaMFJDLJ*%KPP;8p`8x6=t3MaU>ZMiDZKTgm(b3J4!<>ypN+00000 LNkvXXu0mjfF?~`R delta 1500 zcmV<21ta?F1?USQiBL{Q4GJ0x0000DNk~Le0001Z0001Z2nGNE06Anf(vcw`e+h6% zS#tmY4#NNd4#NS*Z>VGd00n(XL_t(|UhSP*qM|SihEve97WTgy)-ql?4JK!p=q3j8t<5ru}H!%q;!mZCkDg3$E8yC=3($xG@!ljV1(;_VBR z086c?Yruzj@6ZOd(ql_CzkHS`f4dmLEVZVt5%21FagC`VruifM#m0&aF}3$!`jUrcfwVtnL` z=HS4yG&SU=BeoP{+s|izf7J;$|FDD*uHz1!Z*|NiH1zl;TyN^ZW;hSI`Oxd)n>ynr z!8}8SQfLQM3AM@9>a&7#!vk{ng-fW~Zef~Zv0$^z2e}8pl6w#K{V^5^hTL*kD|u;L zN6syTwK;jV2TQR{ly}_{tftbFOrKbG7Ayz_U|*IxnG`IuDG1Hwf5D;psW#q^GAWpd zD#$Gtb~EYlzw>SP!4J>{>|s$0C#oy zuv|z53P-t$He6fdve4}LauSc+l;lal(%m8VeB8#;YHwS0?F$k8dwm3)h(B3F7TLN7 zS9}n1&&xkMPpt)if2^gz>F8a`ph^x*PF#c2(3iT>gD2-R!6~Uph3K#L-h#4VmyygU z8iE}mo98P#^8x%2TM7X?Hr!GXQK*oAeIj{S$|MMONNBnq3>*W&&VgX(K(KQH23Wp9 z4|Wa&I|qWD1HsOLVCO)vb0F9`5bPWXb`AtP2ZEgg!OnqTf9F83b0F9`5bPWXb`AtP z2ZEgg!OnqT=it&6$KcYH9{_@#1HsOLVCPhPUufLM!5Q*&7Tm(YFBNX#;F!TJ90;|r zJNnxwQ75-X9`=Jg=aAGWx_?|6I6v(2)q{!FJ<$@Jf(MCUPRIz;4q%7%*bTuk$nrz) zQVV`^?EX{7f55r;V~72mMlYDVG@f>LV%S~K^BQk+(R>D6?Qu{%*Re{-3XasB33o_7 z6<2mfb}TgzEMb~o%MXCUQLYBznMCFoHd%wBkw+rEVMFu))Wzj9=V`QgRxqC- z6+(46u#4H=OG^YZPi)RB1KCVBmwSc9!XzYwh9dIuF(|n*DK*Pt!2)mZc|h{M$TpnN ztlONiS};NK?rfDyD{`;}Ie916=3L7r!6yD+)N7EIzHOK0T+1fG^crkc95x_1Jr_=c z9~TmqfACd^mF<~Qk0B?gK~kSwR&(J00lDEMF zstXZ4Qply0f(0y2Y-8!-!CE#CvL=Wqq1K^Fq2!$+xnLbyAbB3$MybNGCYu00Bc+VO z;LD%}Q#Eg_$kIO91PeUWuVq`wuZvbrwbEr9sj{k;Y^w2FiV>`l8k)KW9E9zoh1bSsloPNmnSwpo66D*gk?s7_Z3w0ZLY00008bP@A*OfBIS8jHmPkDQZp4E>b`mm*kWqw;B4iXJGLIzO zYM}F$288={beyqR!Mif8Cv42af9nEm`z$NNge)!FEUJSk85(xkQVVl5)vK&%fmw>m zMaH+nyfjs7jP8M%DJqs2+Y58AlvkM950ftxH<&mAvq@=zd1Ejg6yBLO3iB=bXJ(DV z21E9hNi$%_ncO3D=D?N{nK!1)f;~U<6Eo()rZ4h^2{U2W2lc@Fxv=exfB4Vq*|6_L z`ZsUq!^S7czr0)lI}g%79bo4{>L>5kz}B=3uU5g{+sp7~9c&Jn4{W~5@M0yGZqBZ- zoU9N$SBe> z-0~u16d|Js8AZq_LPilXe~OS%gp49&6d|Lyzsx3^vp~q42^ocu*5G0@X2YHznWsfIY&ns8TVlhGGug)lHf%8DU#HtJ-%@yaMFJDLJ*%KPP;8p`8x6=t3MaU>ZMiDZKTgm(b3J4!<>ypN+00000 LNkvXXu0mjfZ?I5L delta 1487 zcmV;=1u**S1=|ZDiBL{Q4GJ0x0000DNk~Le0001Z0001Z2nGNE06Anf(vcw`e+h6% zS#tmY4#NNd4#NS*Z>VGd00nSKL_t(|UhSRjlA zg!Y~)t6X$e&9S@l1v*qt@Njb10f6i996tjHKuC|}z{zXI1dm4VqJKX{UON|mK0pnK z)CPS6K1PoflDU>tdhlrF?z2YOf7J*UsV#b`_u=@-LYIhz2alWFebgfRbitDHTpX#j zOMOQoy9E%YO3IO(CRl5kHbC%bkfh_w;?a&i*PFh>5u7Gi2_83e_)U+Sz~d(_H)n9T zVC@yV4#8B^zC)rp{@m$PN^+RsG*w--&Td8PbJ`B2DK1z(Q0pWOf1B8pe@OI?^tOwN zE+$yVvf4ZTcCjgu=(gk-x`^Q5;q(nt>^za^q0Mrl({9;_rWPr81#uh2ZI_Geg3(V} z1j=1c=qE%%iHw@CS9Zomm94=H?@JP2MF~qSk%u*tQhOm|4^J zCid1~C$lsy<>n&*A-v|Ze|H7pX0*qra(OFoleOkOz3WYXIGRspgke^xMnhH~pg+{`+B z+l$uK3<|Cr?Nz@^*|YX3w|zw^VQV;5Cs#iTaAOmQ9vYtpqDkpLk(v_#w zxGn+OJ)OiN9eyLpvx56@r`+q&;diR64uUm3%Z__;LB!oCO%Hdt3}}ONSusx0Z^y*s zattmTywWqH*8t-1f1znwBY1W`lU$M(k>Fi>Z%JK<%YacQi|N@Ex_Q1yXFiA>f`@Am z?eQUY5T~wT{k4hK;ePI!_17d$BA6J4f{jDL#-U*2ICijX!w5DG1sjKgjYGl4p1CWv?Q0{bva=v$V}4)5QnXHoa2cOcCzd+xQqoqgNK91zvvjb5+47g z{yz24C?yAve}=zRcwMCT+_>%~_|IIlL@|Gu<7#;POpSz6!Tm*~JKQPxLU{bR4tM$2 zDEs~~9)~zn^)=AZPnw?Pv1x*bL-J%&DZSXqU`$i=2#$;EXUWl9!LH-4pk0B`*WbzL z#hhT_x%3So=OJ!p%jlv!j*aVNYRo&uQUn99)d^A&e`}pA3YKq3!&F@l;$ks+u|}}) zZ308uK(>FoM`-tQYZMugng7J#x)^)g3@;0PHbp!CoAs0CXD_oq|#@fY$ zPuW$#-w-&2`iy8Olszc23yxC@B`*`VQLeNcQ%rz-BO9H4Vags8b*5SX&k}M`Qm`Uj z{nmB(1x3eVf21>&wdST27p&-j{aeS($=G&ie}~c>CRiCQJ9;$OFoAw4`bC2Nystua zc8D>e8#~koF+I;Ri>g@q81aM^Jq>JtVjd6c&+oQ^*J8$NbfkDC003I zu%c6)=)dhlTB}`lB2}07vQ0Jar5eF8I)+9sdz7}%7M@Pt=(1kPvFskI6da?yPkpab pB`L3z^i(FIn%ckY@x9_t`42?Fn7&;#`H%nr002ovPDHLkV1l*<+D`xg From d96d438a05c7e7dda901a9a530b2c2ba41d1edf9 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 5 Jun 2026 16:56:09 +0200 Subject: [PATCH 16/19] fix(vier-gewinnt): Unalignement of chips Fixed by removing margins --- src/main/kotlin/sc/gui/view/game/Connect4Board.kt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index a2b2bf8..9c0ac53 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -26,16 +26,7 @@ class Connect4Board: GameBoard() { private val gridSize get() = squareSize.div(Connect4Constants.BOARD_WIDTH) // "Length of the smaller side of the window." - val grid: GridPane = GridPane().addClass("grid").apply { - squareSize.listenImmediately { size -> - padding = Insets( - size.toDouble() / 80, - size.toDouble() / 80, - size.toDouble() / 300, - size.toDouble() / 200, - ) - } - } + val grid: GridPane = GridPane().addClass("grid") override val root = hbox { this.alignment = Pos.BOTTOM_CENTER @@ -62,12 +53,12 @@ class Connect4Board: GameBoard() { // this ensures proper sizing of the board (0 until Connect4Constants.BOARD_WIDTH).forEach { y -> //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, y, 0) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) } (0 until Connect4Constants.BOARD_HEIGHT).forEach { y -> //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, 0, y) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) } //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, Connect4Constants.BOARD_WIDTH - 1, Connect4Constants.BOARD_HEIGHT - 1) From 597967ddac26837ae5e9208dc25649defbcb1482 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 5 Jun 2026 17:00:12 +0200 Subject: [PATCH 17/19] refactor(vier-gewinnt): --- .../kotlin/sc/gui/view/game/Connect4Board.kt | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 9c0ac53..2f8ae13 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -1,23 +1,14 @@ package sc.gui.view.game -import javafx.application.Platform -import javafx.beans.binding.DoubleBinding -import javafx.geometry.Insets -import javafx.geometry.Point2D import javafx.geometry.Pos import javafx.scene.Node -import javafx.scene.effect.ColorAdjust -import javafx.scene.effect.Glow import javafx.scene.input.KeyEvent import javafx.scene.layout.GridPane import sc.api.plugins.Coordinates -import sc.gui.util.listenImmediately import sc.gui.view.GameBoard import sc.gui.view.PieceImage -import sc.gui.view.transitionDuration import sc.plugin2098.FieldState import sc.plugin2098.GameState -import sc.plugin2098.util.GameRuleLogic import sc.plugin2098.util.Connect4Constants import tornadofx.* @@ -52,16 +43,12 @@ class Connect4Board: GameBoard() { // this ensures proper sizing of the board (0 until Connect4Constants.BOARD_WIDTH).forEach { y -> - //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, y, 0) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, y, 0) } (0 until Connect4Constants.BOARD_HEIGHT).forEach { y -> - //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) - grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, 0, y) + grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.0 }, 0, y) } - //grid.add(PieceImage(gridSize, "cell").apply { opacity = 0.5 }, Connect4Constants.BOARD_WIDTH - 1, Connect4Constants.BOARD_HEIGHT - 1) - state?.let { state -> state.board.forEach { (pos: Coordinates, field: FieldState) -> @@ -103,5 +90,4 @@ class Connect4Board: GameBoard() { addToGrid(piece, move.position) } } - } \ No newline at end of file From 2b9ca622916804b8fde7b293b2e12b714e8c0b44 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 5 Jun 2026 19:31:18 +0200 Subject: [PATCH 18/19] feat(vier-gewinnt): Improve visibility of possible moves --- src/main/kotlin/sc/gui/view/game/Connect4Board.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 2f8ae13..0eccfaa 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -2,6 +2,7 @@ package sc.gui.view.game import javafx.geometry.Pos import javafx.scene.Node +import javafx.scene.effect.Glow import javafx.scene.input.KeyEvent import javafx.scene.layout.GridPane import sc.api.plugins.Coordinates @@ -81,7 +82,8 @@ class Connect4Board: GameBoard() { val piece = PieceImage(gridSize, "${state.currentTeam}-chip".lowercase()) - piece.opacity = 0.7 + piece.opacity = 0.5 + //piece.effect = Glow(1.0) piece.onLeftClick { sendHumanMove(move) From e07b7b5a2ccf0a557c8b8d419d3ec8b6b76e72f8 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 6 Jun 2026 00:06:14 +0200 Subject: [PATCH 19/19] feat(vier-gewinnt): Mark winning pieces --- ...otlin-compiler-13424373081474350713.salive | 0 backend | 2 +- src/main/kotlin/sc/gui/AppStyle.kt | 2 ++ .../kotlin/sc/gui/view/game/Connect4Board.kt | 21 +++++++++++++++++- .../resources/connect4/chip-gelb-winning.png | Bin 0 -> 873 bytes .../resources/connect4/chip-rot-winning.png | Bin 0 -> 873 bytes 6 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 .kotlin/sessions/kotlin-compiler-13424373081474350713.salive create mode 100644 src/main/resources/connect4/chip-gelb-winning.png create mode 100644 src/main/resources/connect4/chip-rot-winning.png diff --git a/.kotlin/sessions/kotlin-compiler-13424373081474350713.salive b/.kotlin/sessions/kotlin-compiler-13424373081474350713.salive new file mode 100644 index 0000000..e69de29 diff --git a/backend b/backend index 5f739a2..7b67e3a 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 5f739a2425252f94f5df6921f06c4ae4dab93f51 +Subproject commit 7b67e3a100c55ad7ea550217f242d52f805994c6 diff --git a/src/main/kotlin/sc/gui/AppStyle.kt b/src/main/kotlin/sc/gui/AppStyle.kt index 109fef1..612aa8c 100644 --- a/src/main/kotlin/sc/gui/AppStyle.kt +++ b/src/main/kotlin/sc/gui/AppStyle.kt @@ -180,6 +180,8 @@ class AppStyle: Stylesheet() { ".one-chip" { image = resources.url("/connect4/chip-rot.png").toURI() } ".two-chip" { image = resources.url("/connect4/chip-gelb.png").toURI() } + ".one-chip-winning" { image = resources.url("/connect4/chip-rot-winning.png").toURI() } + ".two-chip-winning" { image = resources.url("/connect4/chip-gelb-winning.png").toURI() } ".cell" { image = resources.url("/connect4/cell-debug.png").toURI() } ".grid" { backgroundImage += resources.url("/connect4/board.png").toURI() diff --git a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt index 0eccfaa..dfb1b8f 100644 --- a/src/main/kotlin/sc/gui/view/game/Connect4Board.kt +++ b/src/main/kotlin/sc/gui/view/game/Connect4Board.kt @@ -11,6 +11,7 @@ import sc.gui.view.PieceImage import sc.plugin2098.FieldState import sc.plugin2098.GameState import sc.plugin2098.util.Connect4Constants +import sc.plugin2098.util.GameRuleLogic import tornadofx.* class Connect4Board: GameBoard() { @@ -52,11 +53,29 @@ class Connect4Board: GameBoard() { } state?.let { state -> + + var winningCoords: List = ArrayList() + + state.isOver?.let { isOver -> + if(isOver && state.lastMove != null) { + winningCoords = GameRuleLogic.get4Connected(state.board, state.otherTeam, state.lastMove!!.position) + println(winningCoords.size) + } + } + state.board.forEach { (pos: Coordinates, field: FieldState) -> if(field.team == null) { return@forEach } - val piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip".lowercase() }) + val piece: PieceImage + + if(winningCoords.contains(pos)) { + piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip-winning".lowercase() }) + piece.effect = Glow(1.0) + } else { + piece = PieceImage(gridSize, field.team.let { team -> "${team}-chip".lowercase() }) + } + addToGrid(piece, pos) } } diff --git a/src/main/resources/connect4/chip-gelb-winning.png b/src/main/resources/connect4/chip-gelb-winning.png new file mode 100644 index 0000000000000000000000000000000000000000..3af3c44c7696e0b08cfd21fbbb8c07f3ce0c49ea GIT binary patch literal 873 zcmV-v1D5=WP)Px#1ZP1_K>z@;j|==^1poj52~bQ_MgQ0U00000001Az>%32;bRa{vGi!2kdb!2!6DYwZ940@_JLK~#8N?Uz|@1R)FsC%ONjqn;I4W5C9^ zM+)^7FZh*OilW?pzla+fSF&X^wv5J>(bzH?Gcva-+%n*NO9R6FF&xhrtigAEtWTKO zhOax=_8HcP7n)eM88im3G%)NkWfb1g8D3?C1>VvaT;zExyjN#vji){EW{rU*p7p}J zXZkC=>4%q3bT@c$2HqyU1>T#3*Fk5_YqRjaq&?=Xd6*z*PI)N;W|Xvsyb}Xc3K}zB ziGn#F^bv2w!K63pgcl-V)(dUG_pvbTiPGomXqfk)-sanQn0Tk!<;xW?^Pt{LfSCui zCf}`rsdX8?S_N~zUWRYh!Q?ITg2`tYzF5gAn{_G-XPpYeQ6__Ql*u4nR_VxiS*0W6 zWtEPM7sUmLd{JD0$QQ*0h%ZyBsfBH^7{4yVdnZHH?MaEr@zyI@RWs%|@aPlef$ZCRb$AY?%A~GD$W} zRb*0Zn0ZidCcw;tYBRxxiFfMV+S70w-j{U7!8W`Odh<{l-X`6JKpS2@(ccKO;oUO>D=|8} zSz~BvUWfPU46e=T@Rr8#qF;x1bPTIb8(wK(+4bn~LKEAzRfn%TSoaM+eAmbEPx#1ZP1_K>z@;j|==^1poj52~bQ_MgIT*00000007D?<%<9S00VSVPE-H?0N2V5 zK>z>%32;bRa{vGi!2kdb!2!6DYwZ940@_JLK~#8N?Uz|@1R)FsC%ONjqn;I4W5C9^ zM+)^7FZh*OilW?pzla+fSF&X^wv5J>(bzH?Gcva-+%n*NO9R6FF&xhrtigAEtWTKO zhOax=_8HcP7n)eM88im3G%)NkWfb1g8D3?C1>VvaT;zExyjN#vji){EW{rU*p7p}J zXZkC=>4%q3bT@c$2HqyU1>T#3*Fk5_YqRjaq&?=Xd6*z*PI)N;W|Xvsyb}Xc3K}zB ziGn#F^bv2w!K63pgcl-V)(dUG_pvbTiPGomXqfk)-sanQn0Tk!<;xW?^Pt{LfSCui zCf}`rsdX8?S_N~zUWRYh!Q?ITg2`tYzF5gAn{_G-XPpYeQ6__Ql*u4nR_VxiS*0W6 zWtEPM7sUmLd{JD0$QQ*0h%ZyBsfBH^7{4yVdnZHH?MaEr@zyI@RWs%|@aPlef$ZCRb$AY?%A~GD$W} zRb*0Zn0ZidCcw;tYBRxxiFfMV+S70w-j{U7!8W`Odh<{l-X`6JKpS2@(ccKO;oUO>D=|8} zSz~BvUWfPU46e=T@Rr8#qF;x1bPTIb8(wK(+4bn~LKEAzRfn%TSoaM+eAmbE