11
\$\begingroup\$

The game of xiangqi, also known as Chinese chess, is a chess-like game popular in China, Vietnam, Taiwan, and other East Asian countries. The colours of the two sides in xiangqi are red and black. There are 16 black pieces in xiangqi: the 1 general (G), 2 advisor (A), 2 elephant (E), 2 horse (H), 2 chariot (R), 2 cannon (C), and 5 soldier (S). The vertical lines are known as files (columns), and the horizontal lines are known as ranks (rows).

enter image description here

A notation system partially described in A Manual of Chinese Chess and used by several computer software implementations describes moves in relative terms as follows:

[single-letter piece abbreviation][former file][operator indicating direction of movement][new file, or in the case of purely vertical movement, number of ranks traversed]

The file numbers are counted from each player's right to each player's left. For black, the file numbers are shown in the picture, white background.

In case there are two identical pieces in one file, symbols + (front, higher rank) and - (rear, lower rank) are used instead of former file number. If there are more than two, +(highest rank), 2(2nd highest rank), 3(3rd highest rank), ... is used. It's fine either you use the number or - to mean the lowest rank if more than two exist. If more than one file contain multiple identical pieces, both former file and order can't be omitted, but the piece abbreviation can be omitted.

[former order in the file][single-letter piece abbreviation][operator indicating direction of movement][new file, or in the case of purely vertical movement, number of ranks traversed]
[former order in the file][former file][operator indicating direction of movement][new file, or in the case of purely vertical movement, number of ranks traversed]

A plus sign(+) is used to indicate forward movement. A minus sign(-) is used to indicate backwards movement. A dot or period(.) or equal sign(=) is used to indicate horizontal or lateral movement. For a piece that moves diagonally, the plus or minus sign is used rather than the period.

Thus, if the five soldiers are at (5,6), (7,6), (9,6), (5,8), (7,8), then each of them moving one rank forward are expressed as -5+1, -7+1, S9+1, +5+1, +7+1; two horses at (3,4) and (3,8) each moving to (4,6) are expressed as -H+4 and +H-4; five soldiers are at (5,6), (7,6), (5,4), (5,8), (7,8), then each of them moving one rank forward are expressed as 25+1, -7+1, 35+1, +5+1, +7+1; two horses at (3,4) and (5,8) each moving to (4,6) are expressed as 3H+4 and 5H-4.

Given the current position and destination of the piece to move, and a list/set containing all positions of the same kind of pieces, output its expression. Input format is quie flexible. Shortest code win.

Samples: (use format that first item of the array is moved)

S [(5,6), (7,6), (9,6), (5,8), (7,8)] (5,7) -> -5+1 or -S5+1
S [(7,6), (5,6), (9,6), (5,8), (7,8)] (7,7) -> -7+1 or -S7+1
S [(9,6), (5,6), (7,6), (5,8), (7,8)] (9,7) -> S9+1
S [(5,8), (5,6), (7,6), (9,6), (7,8)] (5,9) -> +5+1 or +S5+1
S [(7,8), (5,6), (7,6), (9,6), (5,8)] (7,9) -> +7+1 or +S7+1
H [(3,4), (3,8)]                      (4,6) -> -H+4
H [(3,8), (3,4)]                      (4,6) -> +H-4
H [(3,8)]                             (4,6) -> H3-4
S [(5,4), (5,8), (7,8), (5,6), (7,6)] (5,5) -> 35+1 or -5+1 or 3S5+1 or -S5+1
S [(5,6), (5,8), (7,8), (5,4), (7,6)] (5,7) -> 25+1 or 2S5+1
S [(5,8), (5,6), (7,8), (5,4), (7,6)] (5,9) -> +5+1 or +S5+1

Pieces starting places and moving rules about from place(x0,y0), to place(x,y) and their distance r, if that helps:

G (5,1)      r=1 & 3<x<7 & y<4
A (4,1)(6,1) r^2=2 & 3<x<7 & y<4
E (3,1)(7,1) r^2=8 & y<6
H (2,1)(8,1) r^2=5
R (1,1)(9,1) (x-x0)(y-y0)=0
C (2,3)(8,3) (x-x0)(y-y0)=0
S (1,4)(3,4)(5,4)(7,4)(9,4)
             r=1 & y>=y0 & (y>y0 | y>5)
\$\endgroup\$
  • 9
    \$\begingroup\$ You should consider adding a minimum formatting, such as 'Task summary / Input / Output / Detailed rules'. This looks like an interesting challenge but is really hard to read as-is. \$\endgroup\$ – Arnauld May 25 '18 at 14:34
7
+150
\$\begingroup\$

Stax, 63 bytes

÷Qa9═wë•Θ╣äUuâ£▼+∙8WU▓kw≈▲!.┤α@∟┐₧╨═ºî⌂z▼0╬⌂6╕ú▄O&⌂¼^Mgâ╗╕.î¡☼:

Run and debug it

Takes input as <position before> <piece type> [<other pieces>] <position after>.

Unpacked:

;x\EE-c:+"=+-"@s|aaE=s,h?$+Ydx]+MEsxh|I@|oc%vcaHX-xn^$'-?'+?z?,b'S<*!,h$*++y+

Run and debug it

Explanation:

The first part calculates the last part: The direction of movement and the resulting file/change in rank:

;x\EE-c:+"=+-"@s|aaE=s,h?$+Yd
;x                            Push position after and before
  \                           Zip -> [[file after, file before], [rank after, rank before]]
   E                          Push files and ranks separately
    E-                        Difference in ranks (after - before)
      c:+                     Copy and get the sign (-1, 0, +1)
         "=+-"@               Index into the string "=+-" (-1 means last one)
               s|a            Get the absolute value of the difference in rank
                  aE=         Check whether file before = file after (pure vertical movement)
                     s        Swap with abs of difference in rank
                      ,h      Get file after
                        ?     If file before = file after:
                                abs(rank after - rank before)
                              Else:
                                file after
                              (this is the number in the end)
                         $    Convert to string
                          +   Concat the sign (-=+) and number.
                           Yd Store in Y register and delete from stack.

The second part calculates the index in the file (+, - or a digit):

x]+MEsxh|I@|oc%vcaHX-xn^$'-?'+?z?
x]+                               Append the position before to the list of other pieces
   ME                             Push list of files, then list of ranks
     s                            Swap the two
      xh|I                        Find indices with file = file before
          @                       Get ranks of these
           |o                     Calculate destination index of each element if the ranks were sorted
             c%v                  Get length of the array - 1
                                    (no. of other pieces in file)
                ca                Get the array of dest. indices again
                  HX              Get the last index (= index of the moving pieces)
                                    and store in register X
                    -             Then subtract from the no. of other pieces
                     xn^          ... and increment the result
                                    -> That's the index from big rank to small
                        $         ... and convert to string
                         '-?      If register X = 0 (lowest rank): "-"
                                  Else: The index from big to small
                            '+?   If index from big to small = 0 (high rank): "+"
                                  Else: Result from above
                               z? If length of array = 1: ""
                                  Else: Result from above

So this means:

  • If there are no other pieces in this rank, ""
  • Else if we have the highest rank, "+"
  • Else if we have the lowest rank, "-"
  • Else, the index of the rank as a string

These conditionals work in a somewhat special way: First, all the conditions are produced, most important to least important, then the true branch of the first condition is made (the index as a string). After this we push the falsy value for each condition, least important to most important, directly before performing the conditional ?.

The third part checks whether we need to add the file number:

,b'S<*!,h$*
,           Get the piece type
 b          Copy both the result of part 2 and the piece type
  'S<       Test whether the piece type is NOT "S"
              (actually less than "S", because all other piece types come before S in ASCII)
     *      Repeat the result of part 2 by the result of this test:
            "" if the piece type is "S"
            the result of part 2 otherwise
      !     Negate this, "" is falsey.
            => This means NOT (part 2 is empty AND NOT piece type is "S")
            => Part 2 is empty iff this is the only piece in the file
            => NOT (only piece in file AND NOT piece type is "S")
            => NOT only piece in file OR piece type is "S"
       ,h   Push the file before
         $  and convert it to string
          * Repeat that string by the result of above check:
            "" unless other pieces in file or piece type is "S"

So, after parts 1-3, the stack looks like this:

<position in the file (+, -, or number)> <piece type> <file if necessary>

And the Y register contains the movement itself (from part 1).

The final part combines these into a single string:

++y+ "<position in the file>" "<piece type>" "<file if necessary>"
+    "<position in the file>" "<piece type><file if necessary>"
 +   "<position in the file><piece type><file if necessary>"
  y  "<position in the file><piece type><file if necessary>" "<movement>"
   + "<position in the file><piece type><file if necessary><movement>"

This final result is implicitly output.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Wow, you just answered a question that went unanswered for 2 years. Nice job! \$\endgroup\$ – user Oct 15 at 22:44
  • \$\begingroup\$ [5,8] "S" [[5,6]] [5,9] file should be omitted \$\endgroup\$ – l4m2 Nov 18 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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