diff --git a/build.gradle.kts b/build.gradle.kts index 52cf9fa..74f0171 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,4 +5,5 @@ plugins { alias(libs.plugins.androidLibrary) apply false alias(libs.plugins.jetbrainsCompose) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.jetbrainsKotlinJvm) apply false } \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 81fdb74..749fefe 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -14,12 +14,13 @@ kotlin { } } } - + jvm("desktop") - + sourceSets { val desktopMain by getting - + val desktopTest by getting + androidMain.dependencies { implementation(libs.compose.ui.tooling.preview) implementation(libs.androidx.activity.compose) @@ -27,7 +28,7 @@ kotlin { commonMain.dependencies { implementation(compose.runtime) implementation(compose.foundation) - implementation(compose.material) + implementation(compose.material3) implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) @@ -35,7 +36,19 @@ kotlin { desktopMain.dependencies { implementation(compose.desktop.currentOs) } + commonTest.dependencies { +// implementation(libs.junit) + implementation(kotlin("test")) + + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) + implementation(compose.uiTest) + } + desktopTest.dependencies { + implementation(compose.desktop.uiTestJUnit4) + implementation(compose.desktop.currentOs) + } } + } android { diff --git a/composeApp/src/commonMain/kotlin/App.kt b/composeApp/src/commonMain/kotlin/App.kt index a1a50dc..32073a4 100644 --- a/composeApp/src/commonMain/kotlin/App.kt +++ b/composeApp/src/commonMain/kotlin/App.kt @@ -2,9 +2,9 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/composeApp/src/commonMain/kotlin/domain/Game.kt b/composeApp/src/commonMain/kotlin/domain/Game.kt new file mode 100644 index 0000000..2e3b4a9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/domain/Game.kt @@ -0,0 +1,21 @@ +package domain + +class Game( + val categories: List, + val grid: Grid, + val horizontalRules: List, + val verticalRules: List +) { + fun areCategoriesValid(): Boolean = categories.mapIndexed { index, category -> + category.any { item -> + categories.filterIndexed { i, _ -> i > index }.any { it.contains(item) } + } + }.none { it } + + fun areRulesViolated(): Boolean = horizontalRules + .map { it.isRuleViolated(grid) } + .reduce { a, b -> a || b } + || verticalRules + .map { it.isRuleViolated(grid) } + .reduce { a, b -> a || b } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/domain/grid.kt b/composeApp/src/commonMain/kotlin/domain/grid.kt new file mode 100644 index 0000000..aae466d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/domain/grid.kt @@ -0,0 +1,21 @@ +package domain + +class GameRow( + val category: ItemCategory, + val cells: List +) : List by cells + +class Grid( + val rows: List +) : List by rows { + fun indexOf(element: Item): Int { + val row = rows.first { it.category == element.category } + return row.indexOfFirst { it.selection == element } + } +} + +class GameCell( + var selection: Item?, + val solution: Item, + val options: List +) diff --git a/composeApp/src/commonMain/kotlin/domain/items.kt b/composeApp/src/commonMain/kotlin/domain/items.kt new file mode 100644 index 0000000..d6a2294 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/domain/items.kt @@ -0,0 +1,44 @@ +@file:OptIn(ExperimentalResourceApi::class) + +package domain + +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.ExperimentalResourceApi + +fun category(block: CategoryContext.() -> Unit): ItemCategory { + class CategoryImpl(val items: MutableList) : + ItemCategory, List by items { + inner class ItemImpl( + override val image: DrawableResource + ) : Item { + override val category: CategoryImpl = this@CategoryImpl + + init { + items.add(this) + } + } + } + + val ctx = object : CategoryContext { + val category = CategoryImpl(mutableListOf()) + + override fun item(image: DrawableResource) { + category.ItemImpl(image) + } + } + ctx.block() + return ctx.category +} + +interface CategoryContext { + fun item(image: DrawableResource) +} + +interface Item { + val category: ItemCategory + + @OptIn(ExperimentalResourceApi::class) + val image: DrawableResource +} + +interface ItemCategory : List diff --git a/composeApp/src/commonMain/kotlin/domain/rules.kt b/composeApp/src/commonMain/kotlin/domain/rules.kt new file mode 100644 index 0000000..95ad9a9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/domain/rules.kt @@ -0,0 +1,69 @@ +package domain + +import kotlin.math.abs + +sealed class GameRule { + abstract fun isRuleViolated(grid: Grid): Boolean +} + +sealed class HorizontalRule : GameRule() + +class NeighbourRule(val a: Item, val b: Item) : HorizontalRule() { + override fun isRuleViolated(grid: Grid): Boolean { + val ia = grid.indexOf(a) + val ib = grid.indexOf(b) + + if (ia == -1 || ib == -1) return false + + return abs(ia - ib) != 1 + } +} + +class OrderRule(val left: Item, val right: Item) : HorizontalRule() { + override fun isRuleViolated(grid: Grid): Boolean { + val il = grid.indexOf(left) + val ir = grid.indexOf(right) + + if (il == -1 || ir == -1) return false + + return ir <= il + } +} + +class TripletRule(val a: Item, val b: Item, val c: Item) : HorizontalRule() { + private fun isNeighbourRuleViolated(ix: Int, iy: Int): Boolean { + if (ix == -1 || iy == -1) return false + return abs(ix - iy) != 1 + } + + override fun isRuleViolated(grid: Grid): Boolean { + val ia = grid.indexOf(a) + val ib = grid.indexOf(b) + val ic = grid.indexOf(c) + + if (ia == -1 && ic == -1) { + return false + } + + if (ia == ib) { + return true + } + + if (isNeighbourRuleViolated(ia, ib) || isNeighbourRuleViolated(ib, ic)) { + return true + } + + return false + } +} + +class VerticalRule(val a: Item, val b: Item) : GameRule() { + override fun isRuleViolated(grid: Grid): Boolean { + val ia = grid.indexOf(a) + val ib = grid.indexOf(b) + + if (ia == -1 || ib == -1) return false + + return ia != ib + } +} diff --git a/composeApp/src/commonMain/kotlin/ui/selector.kt b/composeApp/src/commonMain/kotlin/ui/selector.kt new file mode 100644 index 0000000..e0072c6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ui/selector.kt @@ -0,0 +1,14 @@ +package ui + +import androidx.compose.material3.OutlinedCard +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import domain.Item +import domain.ItemCategory + +@Composable +fun Selector(modifier: Modifier = Modifier, category: ItemCategory, selectedItem: Item, onSelectItem: (Item) -> Unit) { + OutlinedCard(modifier = modifier) { + + } +} \ No newline at end of file diff --git a/composeApp/src/commonTest/kotlin/domain/GameTest.kt b/composeApp/src/commonTest/kotlin/domain/GameTest.kt new file mode 100644 index 0000000..4be936e --- /dev/null +++ b/composeApp/src/commonTest/kotlin/domain/GameTest.kt @@ -0,0 +1,31 @@ +@file:OptIn(ExperimentalResourceApi::class) +package domain + +import org.jetbrains.compose.resources.ExperimentalResourceApi +//import org.junit.Test +import yaep.composeapp.generated.resources.Res +import yaep.composeapp.generated.resources.compose_multiplatform +import kotlin.test.Test +import kotlin.test.fail + +class GameTest { + + @Test + fun areCategoriesValid() { +// Game( +// categories = listOf( +// category { +// item(Res.drawable.compose_multiplatform) +// } +// ), +// Grid( +// +// ) +// ) + } + + @Test + fun areRulesViolated() { + TODO() + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5c84272..804a6dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,30 +1,15 @@ [versions] -agp = "8.3.2" +agp = "8.4.2" android-compileSdk = "34" android-minSdk = "24" android-targetSdk = "34" androidx-activityCompose = "1.9.0" -androidx-appcompat = "1.6.1" -androidx-constraintlayout = "2.1.4" -androidx-core-ktx = "1.13.0" -androidx-espresso-core = "3.5.1" -androidx-material = "1.11.0" -androidx-test-junit = "1.1.5" -compose = "1.6.6" -compose-plugin = "1.6.2" -junit = "4.13.2" -kotlin = "1.9.23" +compose = "1.6.7" +compose-plugin = "1.6.11" +kotlin = "1.9.24" [libraries] -kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } -kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" } -androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } -androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" } -androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } +#junit = { module = "junit:junit", version = "4.13.2"} androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } @@ -33,4 +18,6 @@ compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" androidApplication = { id = "com.android.application", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" } jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } -kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 8de0234..f8b61e6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UnstableApiUsage") + rootProject.name = "YAEP" enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") @@ -28,4 +30,4 @@ dependencyResolutionManagement { } } -include(":composeApp") \ No newline at end of file +include(":composeApp")