Render puzzle (WIP)
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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)) }
|
||||
Selector(
|
||||
category = Animals,
|
||||
options = Animals.items.map { Item(it) },
|
||||
selectedItem = selectedItem,
|
||||
onSelectItem = { selectedItem = it },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
for (row in grid.rows) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
) {
|
||||
for (item in row) {
|
||||
Selector(
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
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()
|
||||
|
||||
class NeighbourClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>) : HorizontalClue() {
|
||||
class NeighbourClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : HorizontalClue() {
|
||||
private val aType = a.itemType
|
||||
private val bType = b.itemType
|
||||
|
||||
@@ -22,7 +26,7 @@ class NeighbourClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
class OrderClue<C:ItemClass<C>>(val left: Item<C>, val right: Item<C>) : HorizontalClue() {
|
||||
class OrderClue<C : ItemClass<C>>(val left: Item<C>, val right: Item<C>) : HorizontalClue() {
|
||||
private val leftType = left.itemType
|
||||
private val rightType = right.itemType
|
||||
|
||||
@@ -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
|
||||
@@ -67,7 +72,7 @@ class TripletClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>, val c: Item<C>
|
||||
}
|
||||
}
|
||||
|
||||
class SameRowClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
class SameRowClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
private val aType = a.itemType
|
||||
private val bType = b.itemType
|
||||
|
||||
@@ -81,7 +86,7 @@ class SameRowClue<C:ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
}
|
||||
}
|
||||
|
||||
class PositionClue<C:ItemClass<C>>(val item: Item<C>, val index: Int) : Clue() {
|
||||
class PositionClue<C : ItemClass<C>>(val item: Item<C>, val index: Int) : Clue() {
|
||||
private val aType = item.itemType
|
||||
|
||||
override fun isRuleViolated(grid: Grid): Boolean {
|
||||
|
||||
@@ -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>>,
|
||||
@@ -10,12 +15,12 @@ class Grid(
|
||||
val rows: List<GameRow<ItemClass<*>>>
|
||||
) : List<GameRow<ItemClass<*>>> by rows {
|
||||
|
||||
fun <C: ItemClass<C>> indexOf(element: C): Int {
|
||||
fun <C : ItemClass<C>> indexOf(element: C): Int {
|
||||
return this[element.companion]
|
||||
.indexOfFirst { it.selection?.itemType == element }
|
||||
}
|
||||
|
||||
operator fun <C: ItemClass<C>> get(itemType: ItemClassCompanion<C>): GameRow<C> {
|
||||
operator fun <C : ItemClass<C>> get(itemType: ItemClassCompanion<C>): GameRow<C> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return rows.first { it.category == itemType } as GameRow<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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!!)
|
||||
}
|
||||
Reference in New Issue
Block a user