Merge pull request #24: one menu per game
Priority is the GameMenu declaration order: solitaire, trick-taking, cards, board. Verified by ci.yml run 114.
This commit is contained in:
@@ -10,8 +10,8 @@ package org.prolibertate.games.game
|
|||||||
*/
|
*/
|
||||||
enum class GameMenu(val label: String, val blurb: String) {
|
enum class GameMenu(val label: String, val blurb: String) {
|
||||||
SOLITAIRE("Solitaire", "Played alone, against the deal or the board."),
|
SOLITAIRE("Solitaire", "Played alone, against the deal or the board."),
|
||||||
CARDS("Card Games", "Anything played with a pack."),
|
|
||||||
TRICK_TAKING("Trick-taking", "Lead, follow, and count what you took."),
|
TRICK_TAKING("Trick-taking", "Lead, follow, and count what you took."),
|
||||||
|
CARDS("Card Games", "Anything else played with a pack."),
|
||||||
BOARD("Board Games", "Boards, pieces and dice."),
|
BOARD("Board Games", "Boards, pieces and dice."),
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -29,9 +29,11 @@ data class GameDescriptor(
|
|||||||
val id: String,
|
val id: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
/**
|
/**
|
||||||
* Every menu this game appears under. A patience is both solitaire and a
|
* Every menu this game could belong to. Klondike really is both a patience
|
||||||
* card game, and Euchre is both a card game and a trick-taking one — so
|
* and a card game, and Euchre really is both a card game and a trick-taking
|
||||||
* this is a set, and the same game is listed in each place it belongs.
|
* one, so this stays a set: it is what the game *is*.
|
||||||
|
*
|
||||||
|
* Where it is *listed* is [home], which is only ever one place.
|
||||||
*/
|
*/
|
||||||
val menus: Set<GameMenu>,
|
val menus: Set<GameMenu>,
|
||||||
val minPlayers: Int,
|
val minPlayers: Int,
|
||||||
@@ -40,7 +42,22 @@ data class GameDescriptor(
|
|||||||
val teamBased: Boolean,
|
val teamBased: Boolean,
|
||||||
val blurb: String,
|
val blurb: String,
|
||||||
val available: Boolean,
|
val available: Boolean,
|
||||||
)
|
) {
|
||||||
|
/**
|
||||||
|
* The one menu this game is listed under.
|
||||||
|
*
|
||||||
|
* The first of its menus in [GameMenu] declaration order, which is the
|
||||||
|
* priority: solitaire first, then trick-taking, then the rest of the card
|
||||||
|
* games, then board games. So a patience is filed under Solitaire rather
|
||||||
|
* than repeated under Card Games, and Euchre under Trick-taking rather than
|
||||||
|
* in both places above it.
|
||||||
|
*
|
||||||
|
* Listing a game twice made every section a partial duplicate of another,
|
||||||
|
* and a menu that repeats itself is harder to scan than a longer one that
|
||||||
|
* does not.
|
||||||
|
*/
|
||||||
|
val home: GameMenu get() = GameMenu.entries.first { menus.contains(it) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The catalogue the menus are built from.
|
* The catalogue the menus are built from.
|
||||||
@@ -207,8 +224,11 @@ object GameCatalog {
|
|||||||
),
|
),
|
||||||
GameDescriptor(
|
GameDescriptor(
|
||||||
id = SEQUENCE,
|
id = SEQUENCE,
|
||||||
|
// Played with two packs, but a board game to anyone who owns it,
|
||||||
|
// and cards outrank board in the priority — so listing it as both
|
||||||
|
// would quietly file it away from where people look for it.
|
||||||
title = "Sequence",
|
title = "Sequence",
|
||||||
menus = setOf(GameMenu.BOARD, GameMenu.CARDS),
|
menus = setOf(GameMenu.BOARD),
|
||||||
minPlayers = 2,
|
minPlayers = 2,
|
||||||
maxPlayers = 12,
|
maxPlayers = 12,
|
||||||
teamBased = true,
|
teamBased = true,
|
||||||
@@ -330,7 +350,7 @@ object GameCatalog {
|
|||||||
menu: GameMenu,
|
menu: GameMenu,
|
||||||
includeComingSoon: Boolean = true,
|
includeComingSoon: Boolean = true,
|
||||||
): List<GameDescriptor> = all.filter {
|
): List<GameDescriptor> = all.filter {
|
||||||
it.menus.contains(menu) && (includeComingSoon || it.available)
|
it.home == menu && (includeComingSoon || it.available)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -40,13 +40,48 @@ class CatalogTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `a game can be in more than one menu`() {
|
fun `a game belongs to several menus but is listed in exactly one`() {
|
||||||
// The whole point of the set. A patience is solitaire and a card game;
|
// The set says what a game is; home says where it is filed. Klondike
|
||||||
// Euchre is a card game and a trick-taking one.
|
// really is a card game, and is still not listed among them.
|
||||||
val klondike = GameCatalog.byId(GameCatalog.KLONDIKE)!!
|
val klondike = GameCatalog.byId(GameCatalog.KLONDIKE)!!
|
||||||
assertTrue(klondike.menus.containsAll(setOf(GameMenu.SOLITAIRE, GameMenu.CARDS)))
|
assertTrue(klondike.menus.containsAll(setOf(GameMenu.SOLITAIRE, GameMenu.CARDS)))
|
||||||
|
assertEquals(GameMenu.SOLITAIRE, klondike.home)
|
||||||
assertTrue(GameCatalog.inMenu(GameMenu.SOLITAIRE).contains(klondike))
|
assertTrue(GameCatalog.inMenu(GameMenu.SOLITAIRE).contains(klondike))
|
||||||
assertTrue(GameCatalog.inMenu(GameMenu.CARDS).contains(klondike))
|
assertFalse(
|
||||||
|
"a patience is filed under Solitaire and nowhere else",
|
||||||
|
GameCatalog.inMenu(GameMenu.CARDS).contains(klondike),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `no game is listed twice`() {
|
||||||
|
// The whole point of the change: every section is a partial duplicate
|
||||||
|
// of another the moment one game appears in two of them.
|
||||||
|
val listed = GameMenu.entries.flatMap { GameCatalog.inMenu(it) }
|
||||||
|
assertEquals("a game is listed more than once", listed.size, listed.distinct().size)
|
||||||
|
assertEquals(GameCatalog.all.size, listed.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `home follows the priority order`() {
|
||||||
|
// Solitaire, then trick-taking, then the rest of the card games, then
|
||||||
|
// board games — which is the declaration order of the enum, and the
|
||||||
|
// only place that order is written down.
|
||||||
|
assertEquals(
|
||||||
|
listOf(GameMenu.SOLITAIRE, GameMenu.TRICK_TAKING, GameMenu.CARDS, GameMenu.BOARD),
|
||||||
|
GameMenu.entries.toList(),
|
||||||
|
)
|
||||||
|
// Solitaire beats cards.
|
||||||
|
assertEquals(
|
||||||
|
GameMenu.SOLITAIRE,
|
||||||
|
GameCatalog.byId(GameCatalog.PEG_SOLITAIRE)!!.home,
|
||||||
|
)
|
||||||
|
// Trick-taking beats cards.
|
||||||
|
assertEquals(GameMenu.TRICK_TAKING, GameCatalog.byId(GameCatalog.HEARTS)!!.home)
|
||||||
|
assertEquals(GameMenu.TRICK_TAKING, GameCatalog.byId(GameCatalog.EUCHRE)!!.home)
|
||||||
|
// Nothing above it, so it stays where it is.
|
||||||
|
assertEquals(GameMenu.CARDS, GameCatalog.byId(GameCatalog.CRIBBAGE)!!.home)
|
||||||
|
assertEquals(GameMenu.BOARD, GameCatalog.byId(GameCatalog.CHESS)!!.home)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -58,12 +93,16 @@ class CatalogTest {
|
|||||||
|
|
||||||
val tricks = GameCatalog.inMenu(GameMenu.TRICK_TAKING)
|
val tricks = GameCatalog.inMenu(GameMenu.TRICK_TAKING)
|
||||||
assertTrue("there should be some", tricks.isNotEmpty())
|
assertTrue("there should be some", tricks.isNotEmpty())
|
||||||
// A sub-menu that listed something its parent did not would be a game
|
// They are card games, and say so — they are simply filed one level in
|
||||||
// you could reach one way and not the other.
|
// rather than listed twice.
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"every trick-taking game is also a card game",
|
"every trick-taking game is also a card game",
|
||||||
tricks.all { it.menus.contains(GameMenu.CARDS) },
|
tricks.all { it.menus.contains(GameMenu.CARDS) },
|
||||||
)
|
)
|
||||||
|
assertTrue(
|
||||||
|
"and none of them is listed among the card games as well",
|
||||||
|
GameCatalog.inMenu(GameMenu.CARDS).none { it.menus.contains(GameMenu.TRICK_TAKING) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user