Render puzzle (WIP)

This commit is contained in:
2024-06-19 23:53:42 +02:00
parent 755c3de295
commit fcbebe802f
7 changed files with 203 additions and 59 deletions

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFF"
android:pathData="M280,680L80,480L280,280L336,336L233,440L727,440L624,336L680,280L880,480L680,680L624,624L727,520L233,520L336,624L280,680Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFF"
android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/>
</vector>

View File

@@ -1,42 +1,163 @@
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
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.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import domain.Animals
import domain.Item
import org.jetbrains.compose.resources.ExperimentalResourceApi
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp
import domain.Grid
import domain.HorizontalClue
import domain.ItemClass
import domain.NeighbourClue
import domain.OrderClue
import domain.SameRowClue
import domain.TripletClue
import domain.generateGame
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import ui.DrawItem
import ui.Selector
import yaep.composeapp.generated.resources.Res
import yaep.composeapp.generated.resources.compose_multiplatform
import yaep.composeapp.generated.resources.neighbour
import yaep.composeapp.generated.resources.order
@Composable
@Preview
fun App(modifier: Modifier = Modifier) {
val size = 6
val game = generateGame()
Row(modifier = modifier) {
PuzzleGrid(modifier = Modifier.weight(1f), game.grid)
PuzzleClues(modifier = Modifier.weight(1f), game.horizontalClues, game.verticalClues)
}
}
@Composable
fun PuzzleGrid(
modifier: Modifier = Modifier,
grid: Grid
) {
Column(modifier = modifier) {
Row {
val options = remember { Animals.items.shuffled().take(size) }
for (option in options) {
var selectedItem by remember { mutableStateOf<Item<Animals>?>(Item(option)) }
for (row in grid.rows) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
for (item in row) {
Selector(
category = Animals,
options = Animals.items.map { Item(it) },
selectedItem = selectedItem,
onSelectItem = { selectedItem = it },
modifier = Modifier.weight(1f)
modifier = Modifier
.padding(4.dp)
.weight(1f),
category = row.category,
options = item.options,
selectedItem = item.selection,
onSelectItem = { item.selection = it }
)
}
}
}
}
}
@Composable
fun PuzzleClues(
modifier: Modifier = Modifier,
horizontalClues: List<HorizontalClue>,
verticalClues: List<SameRowClue<ItemClass<*>>>
) {
Column(modifier = modifier) {
LazyVerticalGrid(
modifier = Modifier.fillMaxWidth().weight(1f),
columns = GridCells.Adaptive(32.dp)
) {
for (clue in horizontalClues.filter { it.isActive }) {
item {
HorizontalClue(
modifier = Modifier.clickable { clue.isActive = false },
clue = clue
)
}
}
for (clue in horizontalClues.filter { !it.isActive }) {
item {
HorizontalClue(
modifier = Modifier
.alpha(0.5f)
.clickable { clue.isActive = true },
clue = clue
)
}
}
}
LazyVerticalGrid(
modifier = Modifier.fillMaxWidth().weight(1f),
columns = GridCells.Adaptive(32.dp)
) {
for (clue in verticalClues.filter { it.isActive }) {
item {
VerticalClue(
modifier = Modifier.clickable { clue.isActive = false },
clue = clue
)
}
}
for (clue in verticalClues.filter { !it.isActive }) {
item {
VerticalClue(
modifier = Modifier
.alpha(0.5f)
.clickable { clue.isActive = true },
clue = clue
)
}
}
}
}
}
@Composable
fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue) {
Column {
when (clue) {
is NeighbourClue<*> -> {
DrawItem(modifier = Modifier.weight(1f), clue.a)
OutlinedCard(modifier = modifier.aspectRatio(1f).weight(1f)) {
Image(
painter = painterResource(Res.drawable.neighbour),
contentDescription = null
)
}
DrawItem(modifier = Modifier.weight(1f), clue.b)
}
is OrderClue<*> -> {
DrawItem(modifier = Modifier.weight(1f), clue.left)
OutlinedCard(modifier = modifier.aspectRatio(1f).weight(1f)) {
Image(painter = painterResource(Res.drawable.order), contentDescription = null)
}
DrawItem(modifier = Modifier.weight(1f), clue.right)
}
is TripletClue<*> -> {
DrawItem(modifier = Modifier.weight(1f), clue.a)
DrawItem(modifier = Modifier.weight(1f), clue.b)
DrawItem(modifier = Modifier.weight(1f), clue.c)
}
}
}
}
@Composable
fun VerticalClue(modifier: Modifier = Modifier, clue: SameRowClue<*>) {
Column(modifier = modifier) {
DrawItem(modifier = Modifier.weight(1f), clue.a)
DrawItem(modifier = Modifier.weight(1f), clue.b)
}
}

View File

@@ -1,9 +1,13 @@
package domain
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import kotlin.math.abs
sealed class Clue {
abstract fun isRuleViolated(grid: Grid): Boolean
var isActive: Boolean by mutableStateOf(true)
}
sealed class HorizontalClue : Clue()
@@ -36,7 +40,8 @@ class OrderClue<C:ItemClass<C>>(val left: Item<C>, val right: Item<C>) : Horizon
}
}
class TripletClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>, val c: Item<C>) : HorizontalClue() {
class TripletClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>, val c: Item<C>) :
HorizontalClue() {
private val aType = a.itemType
private val bType = b.itemType
private val cType = c.itemType

View File

@@ -1,5 +1,10 @@
package domain
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
class GameRow<C : ItemClass<C>>(
val category: ItemClassCompanion<C>,
val options: List<Item<C>>,
@@ -33,10 +38,13 @@ fun List<List<Item<ItemClass<*>>>>.toGrid() = Grid(
)
class GameCell<C : ItemClass<C>>(
var selection: Item<C>?,
selection: Item<C>?,
val solution: Item<C>,
val options: MutableList<Item<C>>
)
options: List<Item<C>>
) {
val options = options.toMutableStateList()
var selection by mutableStateOf(selection)
}
class Item<C : ItemClass<C>>(
val itemType: C,

View File

@@ -89,7 +89,7 @@ enum class Fruit(symbol: String) : ItemClass<Fruit> {
PEAR("🍐"),
MANGO("🥭");
override val symbols: Array<String> = idic(symbol)
override val symbols: Array<String> = arrayOf(symbol)
override val companion
get() = Fruit
@@ -110,7 +110,7 @@ enum class Dessert(symbol: String) : ItemClass<Dessert> {
LOLLIPOP("🍭"),
CUSTARD("🍮");
override val symbols: Array<String> = idic(symbol)
override val symbols: Array<String> = arrayOf(symbol)
override val companion
get() = Dessert
@@ -129,7 +129,7 @@ enum class Transportation(symbol: String) : ItemClass<Transportation> {
TRAM_CAR("🚋"),
BUS("🚌");
override val symbols: Array<String> = idic(symbol)
override val symbols: Array<String> = arrayOf(symbol)
override val companion
get() = Transportation

View File

@@ -2,10 +2,11 @@ package ui
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -14,7 +15,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
@@ -37,7 +37,11 @@ fun <C : ItemClass<C>> Selector(
DrawItem(item = selectedItem, modifier = modifier.clickable { onSelectItem(null) })
} else {
OutlinedCard(modifier = modifier.aspectRatio(1f)) {
LazyHorizontalGrid(rows = GridCells.Fixed(3)) {
LazyVerticalGrid(
columns = GridCells.Fixed(3),
verticalArrangement = Arrangement.Center,
horizontalArrangement = Arrangement.Center
) {
for (option in options) {
item {
DrawItem(
@@ -52,7 +56,10 @@ fun <C : ItemClass<C>> Selector(
}
@Composable
fun <C : ItemClass<C>> DrawItem(item: Item<C>, modifier: Modifier = Modifier) {
fun <C : ItemClass<C>> DrawItem(
modifier: Modifier = Modifier,
item: Item<C>
) {
OutlinedCard(modifier = modifier.aspectRatio(1f)) {
val emoji = item.symbol
@@ -61,7 +68,6 @@ fun <C : ItemClass<C>> DrawItem(item: Item<C>, modifier: Modifier = Modifier) {
Canvas(
modifier = Modifier.fillMaxSize(1f),
onDraw = {
drawRect(Color.Red, size = size)
val textSize = textMeasurer.measure(text = emoji)
val minTextSizeDimension = min(
textSize.size.width,
@@ -87,19 +93,3 @@ fun <C : ItemClass<C>> DrawItem(item: Item<C>, modifier: Modifier = Modifier) {
)
}
}
@Preview
@Composable
fun SelectorPreview() {
val size = 6
val options = remember { Animals.items.shuffled().take(size) }
var selectedItem by remember { mutableStateOf<Item<Animals>?>(Item(options.random())) }
// var selectedItem by remember { mutableStateOf<Item<Animals>?>(null) }
// Selector(
// category = Animals,
// options = Animals.items.map { Item(it) },
// selectedItem = selectedItem,
// onSelectItem = { selectedItem = it }
// )
DrawItem(selectedItem!!)
}