From J Wiki
Jump to navigation Jump to search

The Problem << >>

Entire Solution

input   =: 3|'ABCXYZ'i. 0 2{|: ];._2 freads '02.txt'
outcome =: 3|(-~>:) NB. loss draw win = 0 1 2
NB. score = our choice + 3 * outcome
part1   =: [: +/ >:@{: + 3*outcome/
NB. score = choice to be made + 3 * outcome (now last row)
choice  =: 3|(+<:)
part2   =: [: +/ >:@choice/ + 3*{:
(part1;part2) input  NB. solve part 1 and 2

Part one asks for how many wins are obtained in a rock-paper-scissors tournament. The input lists one game per line, with A or X meaning rock, B or Y meaning paper and C or Z meaning scissors.

Input parsing

This one is a piece of cake for J: we're only interested in the letters, per line, so we cut the lines using ];._2 , splitting using the linefeed, LF, conventionally found at the end of each text file.

Now we have an N by 3 array. As in part one, the left letter is the first player, and the right the second, and we have to do the exact same for every instance, the natural representation would be to have each row correspond to a player, instead of games, as it is now. So we transpose using |: and keep only rows 0 and 2, removing row 1 containing only spaces.

Since we don't care about the letters used, we convert them to numbers. For as far as we know from part 1, the pairs AX, BY and CZ have the same function, i.e. they should get the same value from 0 to 2. We do so by looking up their index in the dictionary 'ABCXYZ' using i., and wrapping around to 0 at 3, by applying 3| for modulo 3. Eventually, that leaves us with a 2 row array, with the top row being the adversary's choice, and the bottom row, for part 1 at least, our choice. This encoding/shaping makes it easy to operate on the entirety of the rows later.

Part 1

An important insight is that the game is cyclical:

 Rock < Paper < Scissors < Rock ==> 0 < 1 < 2 < 0 

Encoding outcomes loss, draw and win as 0, 1, 2, and taking as left argument the adversary's choice, and as right argument our choice, the input-output table to be encoded is thus:

   outcome"0 table~ 0 1 2
outcome"00 1 2
0        1 2 0
1        0 1 2
2        2 0 1

In this, "0 is only required to properly create the table, table being defined in the standard library (this is a peculiarity of how the the table adverb (/) used by table influences the rank at which the verb is applied to: x u/ y is equivalent to x u"(lu,_) y). In actual usage, outcome will work on entire lists of inputs instead of on single elements.

When solving, I created the table by hand, and experimented with the expression above until I found an implementation of outcome that produced the table above. One way of defining outcome I came up with is the fork 3 | (-~>:).

Now the final output should be the sum (+/) of our choice (>:@{:) plus three times the outcome (outcome/, outcome being applied between our adversary's choice in the first row of the input and our choice in the last row of the input). As you might have noticed, that spells in J pretty much as it does in English.

Part 2

Oh no, we misunderstood: XYZ indicate the expected outcome (loss, draw, win) instead, and we are to choose the corresponding one of Rock, Paper and Scissors... The scoring function remains the same, so we keep the structure of the solution we had before:

part1=: [: +/ >:@{:      + 3*outcome/ NB. {: being choice
part2=: [: +/ >:@choice/ + 3*{:       NB. {: being required outcome

Now, we need to find an implementation of choice such that it picks the right choice of Rock, Paper, Scissors, or here 0, 1 or 2 given the adversary's choice (left argument) and the required outcome (right argument).

   choice"0 table~ 0 1 2
choice"00 1 2
0       2 0 1
1       0 1 2
2       1 2 0

Through some experimentation I landed on the fork 3|(+<:), being pleasantly similar to outcome. Fun thing to note is the choice and outcome tables are rotated versions of the same table.