15
\$\begingroup\$

Carcassonne is a tile-based game, where the objective is to construct Roads, Cities and Monasteries, in order to score points. The game works by players taking turns to draw and place tiles to construct a landscape, then claiming roads, cities and monasteries. An example landscape is:

Example Landscape

There are \$19\$ distinct tiles (ignoring rotations), each of which contains at least one feature (Road, City or Monastery):

All tiles

Also, notice that the landscape must be consistent. This means that roads must connect to other roads, city edges must connect to other city edges and fields must connect to fields. Therefore, these tiles are inconsistent:

Inconsistent tiles

In a normal game of Carcassonne, each feature in the landscape is claimed by a player, and contributes to their final score. However, in this version, we will change the rules slightly (primarily for people who already know the standard Carcassonne rules):

  • There will only be \$1\$ player
  • The player will automatically own every feature in the landscape
  • There will be no "badge" score for cities
    • Notice that in some of the tiles in the pictures have badge symbols on them. In this challenge, we're ignoring them.

In a landscape, each feature is either complete or incomplete:

  • A complete monastery is one where it's tile is surrounded by \$8\$ tiles. This landscape contains one complete monastery (in the center) and one incomplete monastery (Top center).
  • A complete city is one fully enclosed by walls. This landscape has \$2\$ complete cities and \$2\$ incomplete cities.
  • A complete road is one where both ends terminate at either a city, village or by forming a loop. This landscape has \$2\$ complete roads and \$2\$ incomplete roads.

A landscape is scored as follows:

  • For every complete city, the player scores \$2\$ points for every tile containing that city. Therefore this city scores \$4\$ points, and this city scores \$18\$ points.
  • For every complete road, the player scores \$1\$ point for every tile containing that road. Therefore this road scores \$6\$ points, and this road scores \$2\$.
  • For every complete monastery, the player scores \$9\$ points, such as this monastery.

  • For every incomplete city, the player scores \$1\$ point for every tile containing that city. This landscape contains two incomplete cities, one scoring \$2\$ and the other \$5\$.

  • For every incomplete road, the player scores \$1\$ point for each tile containing that road. This landscape contains three incomplete roads, scoring \$1\$, \$2\$ and \$3\$ points.
  • For every incomplete monastery, the player scores \$1\$ point for each tile that neighbours the monastery, plus \$1\$ for the monastery itself. This landscape contains \$3\$ monasteries, scoring \$2\$, \$5\$ and \$7\$.

Take this landscape:

Example landscape 2

This landscape contains \$1\$ complete city, \$2\$ incomplete cities, \$4\$ complete roads, \$2\$ incomplete roads and \$1\$ incomplete monastery which scores \$4 + (3+3) + (3+2+6+2) + (4+2) + 4 = 33\$ points.


To avoid this challenge being about image processing, we can translate each tile into a list containing \$5\$ values, according to this legend:

[North edge, East edge, South edge, West Edge, # of cities]

0: Field
1: Road
2: City

For instance, this tile can be described as [2, 0, 1, 1, 1]. Using this legend, we can describe each tile uniquely, and it's rotations are rotations of the first four elements. The entire grid can be described as a rectangular matrix, with a \$20^\text{th}\$ distinct value for an empty square. Translating the first landscape into this format, we get:

[
 [             [],              [], [1, 1, 0, 0, 0], [1, 1, 2, 1, 1], [0, 1, 0, 1, 0],              [],              []],
 [[1, 0, 1, 0, 0],              [], [0, 0, 0, 0, 0], [2, 0, 2, 0, 2],              [], [0, 2, 2, 2, 1], [0, 0, 0, 2, 1]],
 [[1, 1, 0, 1, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 0], [2, 2, 0, 0, 1], [2, 2, 0, 2, 1], [2, 0, 0, 2, 1],              []]
]

using [] to represent an empty square. The complete list of tiles (ignoring rotations) in the same grid as the second image is

[1, 0, 1, 0, 0] [0, 0, 1, 1, 0] [2, 1, 1, 1, 1] [0, 1, 1, 1, 0] [2, 0, 0, 0, 1]
[2, 2, 0, 2, 1] [0, 0, 0, 0, 0] [2, 2, 2, 2, 1] [2, 2, 0, 0, 1] [2, 1, 1, 2, 1]
[2, 2, 0, 0, 2] [0, 0, 1, 0, 0] [2, 0, 1, 1, 1] [2, 1, 1, 0, 1] [0, 2, 0, 2, 1]
[1, 1, 1, 1, 0] [2, 1, 0, 1, 1] [2, 2, 1, 2, 1] [2, 0, 2, 0, 2]

Note that the only way to distinguish tiles with monasteries and villages is by the number of roads/cities on the tile, and that there is no specific indicator that a tile has a monastery/village aside from the roads.


Your task is to take a matrix of lists in the above format representing a landscape and to output the score the player would have if they claimed all the features, complete or incomplete, in the landscape. You may take input in any convenient method, may choose any three consistent values to represent Field, City and Road edges and may use any value to represent the empty squares that isn't already a tile. Output must be an integer represented in your language's most natural format. If you aren't sure about an I/O format, just ask.

In addition, the inputs will always have consistent landscapes, and will always have at least 1 tile. Finally, the tiles will not be constrained by the tile availability (like in the real game).

This is so the shortest code in bytes wins.

Test cases

Each test case has the matrix followed by the score, with the score breakdown underneath with abbreviations.

[[[], [], [1, 1, 0, 0, 0], [1, 1, 2, 1, 1], [0, 1, 0, 1, 0], [], []], [[1, 0, 1, 0, 0], [], [0, 0, 0, 0, 0], [2, 0, 2, 0, 2], [], [0, 2, 2, 2, 1], [0, 0, 0, 2, 1]], [[1, 1, 0, 1, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 0], [2, 2, 0, 0, 1], [2, 2, 0, 2, 1], [2, 0, 0, 2, 1], []]] -> 32
1 c. city (4), 1 i. city (6), 6 i. roads (10), 2 i. monasteries (12)

[[[0, 2, 2, 2, 1], [1, 0, 1, 2, 1], [0, 1, 1, 0, 0], [2, 2, 2, 1, 1]], [[2, 1, 1, 2, 1], [1, 0, 0, 1, 0], [1, 1, 1, 0, 0], [2, 2, 2, 1, 1]], [[1, 1, 1, 0, 0], [0, 2, 1, 1, 1], [1, 0, 1, 2, 1], [2, 1, 1, 0, 1]], [[1, 0, 0, 0, 0], [1, 1, 0, 0, 0], [1, 0, 0, 1, 0], [1, 0, 1, 0, 0]]] -> 33
1 c. city (4), 2 i. cities (6), 4 c. roads (13), 2 i. roads (6), 1 i. monastery (4)

[[[0, 1, 1, 0, 0], [2, 1, 0, 1, 1], [1, 2, 1, 1, 1], [0, 2, 0, 2, 1], [0, 1, 1, 2, 1]], [[], [], [1, 0, 2, 1, 1], [0, 1, 1, 0, 0], [1, 2, 1, 1, 1]], [[2, 1, 0, 1, 1], [1, 2, 1, 1, 0], [2, 1, 1, 2, 1], [], [1, 1, 0, 1, 0]], [[], [1, 0, 0, 1, 1], [1, 0, 1, 0, 0], [2, 2, 0, 0, 2], [0, 1, 1, 2, 1]]] -> 39
2 c. cities (12), 4 i. cities (5), 1 c. road (2), 12 i. roads (20)

[[[2, 1, 1, 0, 1]]] -> 2
1 i. city (1), 1 i. road (1)

[[[1, 2, 1, 1, 1], [0, 0, 0, 2, 1], [], [1, 1, 0, 1, 0], [2, 2, 2, 1, 1]], [[1, 0, 1, 0, 0], [], [2, 0, 2, 0, 2], [0, 0, 0, 0, 0], []], [[1, 1, 2, 2, 1], [0, 1, 0, 1, 0], [2, 0, 1, 1, 1], [], []], [[], [], [2, 2, 1, 2, 1], [], []]] -> 29
2 c. cities (8), 4 i. cities (4), 2 c. roads (8), 4 i. roads (4), 1 i. monastery (5)

[[[], [0, 0, 2, 0, 1], [], [], [0, 0, 2, 0, 1], [], [], []], [[], [2, 1, 1, 0, 1], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [2, 0, 1, 1, 1], [], [], []], [[], [1, 0, 1, 0, 0], [0, 2, 2, 0, 1], [0, 2, 2, 2, 1], [1, 1, 2, 2, 1], [0, 0, 1, 1, 0], [], []], [[0, 1, 1, 0, 0], [1, 2, 2, 1, 1], [2, 0, 2, 2, 1], [2, 0, 2, 0, 1], [2, 2, 0, 0, 2], [1, 1, 2, 2, 1], [0, 1, 2, 1, 1], [0, 0, 1, 1, 0]], [[1, 1, 1, 0, 0], [2, 2, 2, 1, 1], [2, 2, 2, 2, 1], [2, 1, 2, 2, 1], [0, 0, 0, 1, 0], [2, 2, 0, 0, 1], [2, 1, 2, 2, 1], [1, 0, 1, 1, 0]], [[1, 0, 1, 0, 0], [2, 2, 0, 0, 1], [2, 0, 2, 2, 1], [2, 0, 2, 0, 1], [0, 0, 0, 0, 0], [0, 2, 2, 0, 1], [2, 1, 1, 2, 1], [1, 0, 0, 1, 0]], [[1, 1, 0, 0, 0], [0, 0, 1, 1, 0], [2, 2, 0, 0, 1], [2, 2, 0, 2, 1], [0, 2, 0, 2, 1], [2, 1, 1, 2, 1], [1, 0, 0, 1, 0], []], [[], [1, 1, 0, 0, 0], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [1, 0, 0, 1, 0], [], []]] -> 106
3 c. cities (54), 5 c. roads (34), 2 c. monasteries (18)

Note that in the largest city in the final example, the tile [2, 2, 0, 0, 2] has two sections of the city, but is only a single tile. Therefore, it only counts for 2 points, not 4.

If you have a copy of Carcassonne, I'd highly recommend building the test cases, if only to help visualise them

\$\endgroup\$
2
  • \$\begingroup\$ Sandbox (deleted). Don't hesitate to provide any and all feedback \$\endgroup\$ Jun 9, 2020 at 19:42
  • \$\begingroup\$ I knew tiles like this one existed but I think that layout (with or without the Trader's icon) only exists in expansions, not the originals. Otherwise, it may have broken the encoding scheme. \$\endgroup\$ Nov 23, 2020 at 18:22

1 Answer 1

14
+200
\$\begingroup\$

Charcoal, 306 246 bytes

Eθ⭆ι⎇λ⊟λψ≔ΣEθΣEι∨⁼²№λ¹№λ¹ζFLθFL§θι«Jκι¿›²Σ∨§§θικ²≧⁺⊕LΦKM℅λζ»FLθFL§θι«Jκι¿⁼²ΣKK«≔⌕§§θικ²δ≔δεM✳⁻χ⊗ε¿ΣKK«W∨⁻ⅉι⁻ⅈκ«≧⁻ΣKKεW∨›²§§§θⅉⅈε¬Σ§KVε≦⊕εM✳⁻χ⊗ε»¿¬⁼﹪⁺²ε⁴δ1»»»Wφ«≔⁰φFLθFL§θκ«Jλκ≔ΣKKδ≔§§θκλη≔∧δΦ⌕A粬℅§KVμε¿ε«≔¹φ¿‹Lεδ«§≔η⊟ε⁰≔¹δ1»ψ≧⁺δζ»»»≧⁺⊗ΣEKAΣιζ⎚Iζ

Try it online! Link is to verbose version of code. Explanation:

Eθ⭆ι⎇λ⊟λψ

Start by printing the number of cities on each tile so that the tiles can be walked using canvas operations rather than array indexing. Just getting the number of cities via AtIndex(AtIndex(AtIndex(q, i), k), 4) normally takes 7 bytes, whereas it can be read from the canvas in only 6 via JumpTo(k, i); and then Sum(Peek()) (although obviously we can only do that as a separate statement). We can also directly inspect adjacent cells using PeekMoore() and PeekVonNeumann() to see whether they are empty tiles (empty tiles will be a null byte or an empty string for out-of-range tiles, but both have an ordinal of 0). Furthermore, removing the city count data from the input array makes the code to deal with the features cleaner (i.e. shorter).

≔ΣEθΣEEι№λ¹∨⁼²λλζ

Start off by counting the roads. Complete and incomplete roads both score their length, so the only special case here is that a tile with two roads only scores 1.

FLθFL§θι«Jκι¿›²Σ∨§§θικ²≧⁺⊕LΦKM℅λζ»

Next, find the monasteries, whose tiles have with no features apart from an optional road. For each monastery, simply count one more than the number of adjacent tiles, since complete monasteries count 9 anyway.

FLθFL§θι«

We need to check whether tiles with two cities are connected. Loop over all of the tiles.

Jκι¿⁼²ΣKK«

Is this a tile of two cities?

≔⌕§§θικ²δ≔δεM✳⁻χ⊗ε

Pick one of the city directions and move one step.

¿ΣKK«

Is there city here?

W∨⁻ⅉι⁻ⅈκ«

Repeat until we get back to the original tile.

≧⁻ΣKKε

If this is a different tile of two cities, then immediately U-turn, otherwise turn left. This allows us to perform a left-following maze traversal.

W∨›²§§§θⅉⅈε¬Σ§KVε≦⊕ε

Turn right until there is a connected city.

M✳⁻χ⊗ε

Move to the next city.

»¿¬⁼﹪⁺²ε⁴δ1»»»

If we did not arrive in the opposite direction to which we left, then the two cities on this tile are actually connected, so count them as a single city.

Wφ«≔⁰φ

Next, we need to find tiles with incomplete cities. Use a variable with a predefined non-zero value as a loop variable, but immediately set it to zero inside the loop.

FLθFL§θκ«

Loop over each tile.

Jλκ≔ΣKKδ≔§§θκλη≔∧δΦ⌕A粬℅§KVμε

Check for adjacent tiles in the appropriate directions. We can assume that these tiles will be city tiles if they exist, but if they don't exist then this tile is part of an incomplete city.

¿ε«≔¹φ

If so, then note that we had an incomplete city for later.

¿‹Lεδ«

If this tile has two cities, but only one was found to be incomplete, then...

§≔η⊟ε⁰

... mark that direction as a field, and...

≔¹δ

... reduce the score for this tile to 1, and...

1

... reduce the number of remaining cities to 1.

»ψ

Otherwise delete the tile completely. This will cause any adjacent city tiles to be (re)evaluated for incompleteness.

≧⁺δζ»»»

Score the number of cities deleted.

≧⁺⊗ΣEKAΣιζ

Score two for any remaining cities on the remaining tiles, as these must now be complete cities.

⎚Iζ

Clear the canvas and output the final score.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.