diff --git a/composeApp/src/androidMain/kotlin/ui/theme/emoji.kt b/composeApp/src/androidMain/kotlin/ui/theme/emoji.kt new file mode 100644 index 0000000..5f40962 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/ui/theme/emoji.kt @@ -0,0 +1,5 @@ +package ui.theme + +import androidx.compose.ui.text.font.FontFamily + +actual val emojiFontFamily: FontFamily? = null \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/domain/Game.kt b/composeApp/src/commonMain/kotlin/domain/Game.kt index cbdb400..222af70 100644 --- a/composeApp/src/commonMain/kotlin/domain/Game.kt +++ b/composeApp/src/commonMain/kotlin/domain/Game.kt @@ -1,5 +1,7 @@ package domain +import androidx.compose.ui.util.fastAny + class Game( val grid: Grid, val clues: List @@ -18,7 +20,6 @@ class Game( gameCell.options.remove(position.item) } } - } } @@ -34,6 +35,5 @@ class Game( } fun areRulesViolated(): Boolean = clues - .map { it.isRuleViolated(grid) } - .reduce { a, b -> a || b } + .any { it.isRuleViolated(grid) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/domain/generator.kt b/composeApp/src/commonMain/kotlin/domain/generator.kt index 9304fc5..daec5a9 100644 --- a/composeApp/src/commonMain/kotlin/domain/generator.kt +++ b/composeApp/src/commonMain/kotlin/domain/generator.kt @@ -9,7 +9,7 @@ fun generateGame(size: Int = 6): Game { // Generate a random puzzle instance. val classes = ItemClass.randomClasses(size) - val grid: List>>> = classes.map { it -> + val grid: List>>> = classes.map { it.randomItems(size).map { item -> Item(item) } } @@ -18,6 +18,10 @@ fun generateGame(size: Int = 6): Game { // if there are 5 houses, there are 5 possible clues of the form "Person A lives in // house B", 8 possible clues of the form "Person A lives next to house B", and so on.) var clues = getAllClues(grid).shuffled() +// var positionClues: MutableSet>> = grid.flatMap { row -> +// row.mapIndexed { i, item -> PositionClue(item, i) } +// }.toMutableSet() + var i = 0 @@ -37,12 +41,14 @@ fun generateGame(size: Int = 6): Game { // (You can speed this up by removing clues in batches rather than one at a time, but it makes the algorithm more complicated to describe.) } -private fun solve(grid: Grid, clues: List): PuzzleSolution { +private fun solve( + grid: Grid, + clues: Collection +): PuzzleSolution { // Start with a grid where each cell is a list of all possible items. // First, set the positions of the items that are already known. - val positionClues = clues.filterIsInstance>>().toSet() - positionClues.forEach { position -> + clues.filterIsInstance>>().forEach { position -> val row = grid[position.item.itemType.companion] row.forEachIndexed { index, gameCell -> if (index == position.index) { @@ -56,9 +62,10 @@ private fun solve(grid: Grid, clues: List): PuzzleSolution { // For each clue, remove any items that violate the clue. // If any cell has only one item left, remove that item from all other cells. // Repeat until no more items can be removed. - val otherClues = clues - positionClues - var removedOptions = false + val otherClues = clues.filter { it !is PositionClue<*> } + var removedOptions: Boolean do { + removedOptions = false grid.forEach { row -> removedOptions = row.tryOptionsForClues(grid, otherClues) || removedOptions } @@ -112,15 +119,17 @@ fun > GameRow.cleanupOptions(cell: GameCell) { } } -private fun getAllClues(rows: List>>>): MutableList { - val clues = mutableListOf() - // For optimization reasons we want the positional clues first - rows.forEach { row -> - row.forEachIndexed { i, item -> - clues.add(PositionClue(item, i)) - } - } - rows.forEachIndexed { i, columns -> +private fun getAllClues(rows: List>>>): MutableSet { + val clues = mutableSetOf() +// rows.forEach { row -> +// row.forEachIndexed { i, item -> +// clues.add(PositionClue(item, i)) +// } +// } + clues.add(PositionClue(rows.random().first(), 0)) + clues.add(PositionClue(rows.random()[3], 3)) + + rows.forEach { columns -> columns.forEachIndexed { j, item -> // Clue: Neighbours if (j > 0) { diff --git a/composeApp/src/commonMain/kotlin/domain/grid.kt b/composeApp/src/commonMain/kotlin/domain/grid.kt index 2df5914..b5f3a2e 100644 --- a/composeApp/src/commonMain/kotlin/domain/grid.kt +++ b/composeApp/src/commonMain/kotlin/domain/grid.kt @@ -30,9 +30,9 @@ class Grid( fun List>>>.toGrid() = Grid( map { row -> GameRow( - row.first().itemType.companion, - row, - row.map { GameCell(selection = null, solution = it, options = row.toMutableList()) } + category = row.first().itemType.companion, + options = row, + cells = row.map { GameCell(selection = null, solution = it, options = row.toMutableList()) } ) } ) diff --git a/composeApp/src/commonMain/kotlin/ui/selector.kt b/composeApp/src/commonMain/kotlin/ui/selector.kt index ac7808b..b02754f 100644 --- a/composeApp/src/commonMain/kotlin/ui/selector.kt +++ b/composeApp/src/commonMain/kotlin/ui/selector.kt @@ -23,6 +23,7 @@ import domain.Item import domain.ItemClass import domain.ItemClassCompanion import org.jetbrains.compose.ui.tooling.preview.Preview +import ui.theme.emojiFontFamily import kotlin.math.min @Composable @@ -86,7 +87,7 @@ fun > DrawItem( drawText( textMeasurer = textMeasurer, text = emoji, - style = TextStyle(fontSize = fontSize), + style = TextStyle(fontSize = fontSize, fontFamily = emojiFontFamily ?: TextStyle.Default.fontFamily), topLeft = offset ) } diff --git a/composeApp/src/commonMain/kotlin/ui/theme/emoji.kt b/composeApp/src/commonMain/kotlin/ui/theme/emoji.kt new file mode 100644 index 0000000..e6c618c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ui/theme/emoji.kt @@ -0,0 +1,5 @@ +package ui.theme + +import androidx.compose.ui.text.font.FontFamily + +expect val emojiFontFamily: FontFamily? \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/ui/theme/emoji.kt b/composeApp/src/desktopMain/kotlin/ui/theme/emoji.kt new file mode 100644 index 0000000..9f7f8c3 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ui/theme/emoji.kt @@ -0,0 +1,14 @@ +package ui.theme + +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.platform.Font + +actual val emojiFontFamily: FontFamily? = FontFamily( + Font( + resource = "NotoColorEmoji-Regular.ttf", + weight = FontWeight.W400, + style = FontStyle.Normal + ) +) \ No newline at end of file diff --git a/composeApp/src/desktopMain/resources/NotoColorEmoji-Regular.ttf b/composeApp/src/desktopMain/resources/NotoColorEmoji-Regular.ttf new file mode 100644 index 0000000..05b42fd Binary files /dev/null and b/composeApp/src/desktopMain/resources/NotoColorEmoji-Regular.ttf differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e9f1dd..4bc2f1a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.3.2" +agp = "8.5.0" android-compileSdk = "34" android-minSdk = "24" android-targetSdk = "34" diff --git a/proguard-rules.pro b/proguard-rules.pro new file mode 100644 index 0000000..2345d65 --- /dev/null +++ b/proguard-rules.pro @@ -0,0 +1,13 @@ +-libraryjars /jmods/java.base.jmod(!**.jar;!module-info.class) + +-keepclasseswithmembers public class MainKt { + public static void main(java.lang.String[]); +} + +-dontwarn kotlinx.coroutines.debug.* + +-keep class kotlin.** { *; } +-keep class kotlinx.coroutines.** { *; } +-keep class org.jetbrains.skia.** { *; } +-keep class org.jetbrains.skiko.** { *; } +-dontwarn kotlinx.datetime.**