aboutsummaryrefslogtreecommitdiff
path: root/include/ptn.c
blob: 3d9c0a5d488a7e830d8052790a2660c10663605b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "ptn.h"

#define ASSERT_NONEMPTY { if (ptn == 0 || *ptn == 0) return PTN_INVALID; }
#define ASSERT_MORE     { if (*ptn == 0) return PTN_INVALID; }

uint8_t
likely_move(char *ptn) {
  if (ptn == 0) return 0;
  for (;;ptn++) {
    switch (*ptn) {
      case '+':
      case '-':
      case '>':
      case '<': return 1;
      case 0:   return 0;
    }
  }
}

enum E_RESULT
parse_place(const uint8_t board_size, char *ptn,
            uint8_t *out_location, enum STONE_VARIANT *out_stone) {

  if (board_size < 5 || board_size > 6) return PTN_INVALID;

  ASSERT_NONEMPTY;

  *out_stone = STONE_FLAT;
  switch (*ptn) {
    case 'C' : { ptn++; *out_stone = STONE_CAPSTONE; break; };
    case 'S' : { ptn++; *out_stone = STONE_STANDING; break; };
    case 'F' : { ptn++; break; };
  }

  ASSERT_MORE;

  if ( (*ptn < 'a') || (*ptn > '`' + board_size) ) return PTN_INVALID;
  *out_location = *ptn - 'a';

  ptn++; ASSERT_MORE;

  if ( (*ptn < '1') || (*ptn > board_size + '0') ) return PTN_INVALID;
  *out_location += board_size * (*ptn - '1');

  if (*(++ptn) > 0) return PTN_INVALID;

  return PTN_VALID;
}

enum E_RESULT
parse_move(const uint8_t board_size, char *ptn,
           uint8_t *out_location, enum MOVE_DIRECTION *out_direction,
           uint8_t *out_steps, uint8_t out_drops[5]) {

  if (board_size < 5 || board_size > 6) return PTN_INVALID;

  ASSERT_NONEMPTY;

  uint8_t picked_up = 1;

  if ( (*ptn >= '1') && (*ptn <= '0' + board_size)) {
    picked_up = *ptn - '0';
    ptn++; ASSERT_MORE;
  }

  if ( (*ptn < 'a') || (*ptn > '`' + board_size) ) return PTN_INVALID;
  *out_location = *ptn - 'a';

  ptn++; ASSERT_MORE;

  if ( (*ptn < '1') || (*ptn > board_size + '0') ) return PTN_INVALID;
  *out_location += board_size * (*ptn - '1');

  ptn++; ASSERT_MORE;

  switch (*ptn) {
    case '+': { *out_direction = M_UP;    break; }
    case '-': { *out_direction = M_DOWN;  break; }
    case '<': { *out_direction = M_LEFT;  break; }
    case '>': { *out_direction = M_RIGHT; break; }
    default: return PTN_INVALID;
  }

  ptn++;
  // Handle the case '[1]<column><row><direction>' as
  // '1<column><row><direction>1' for convenience
  if (*ptn == 0) {
    if (picked_up == 1) {
      *out_steps = 1;
      out_drops[0] = 1;
    } else {
      return PTN_INVALID;
    }
  }

  *out_steps = 0;
  uint8_t total = 0;
  while (*ptn) {
      if ( (*ptn < '1') || (*ptn > '0' + board_size) ) {
        return PTN_INVALID;
      }

      if ( (*out_steps + 1 >= board_size) && *ptn) return PTN_INVALID;

      out_drops[*out_steps] = *ptn - '0';
      total += out_drops[*out_steps];
      *out_steps += 1;
      ptn++;
  }

  if ( total != picked_up ) return PTN_INVALID;

  return PTN_VALID;
}

void
generate_place(const uint8_t board_size, const uint8_t in_location,
               const enum STONE_VARIANT in_stone, char out_ptn[4]) {
  switch (in_stone) {
    case STONE_FLAT: { break; }
    case STONE_STANDING: { *out_ptn = 'S'; out_ptn++; break; }
    case STONE_CAPSTONE: { *out_ptn = 'C'; out_ptn++; break; }
  }
  *out_ptn = 'a' + (in_location % board_size); out_ptn++;
  *out_ptn = '1' + (in_location / board_size); out_ptn++;
  *out_ptn = 0;
}

void
generate_move(const uint8_t board_size, const uint8_t in_location,
              const enum MOVE_DIRECTION in_direction,
              const uint8_t in_steps, const uint8_t in_drops[5],
              char out_ptn[10]) {
  uint8_t total = 0;
  for (uint8_t k = 0; k<in_steps; k++) total+=in_drops[k];
  if (total > 1) {
    *out_ptn = '0' + total; out_ptn++;
  }

  *out_ptn = 'a' + (in_location % board_size); out_ptn++;
  *out_ptn = '1' + (in_location / board_size); out_ptn++;

  switch (in_direction) {
    case M_UP:    { *out_ptn = '+'; break; }
    case M_DOWN:  { *out_ptn = '-'; break; }
    case M_LEFT:  { *out_ptn = '<'; break; }
    case M_RIGHT: { *out_ptn = '>'; break; }
  }; out_ptn++;


  for (uint8_t k = 0; (total > 1) && (k < in_steps); k++) {
    *out_ptn = '0' + in_drops[k]; out_ptn++;
  }

  *out_ptn = 0;
}