Implementation of Lucifers Pastime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
9.8KB

  1. #!/usr/bin/perl
  2. use warnings;
  3. use strict;
  4. use utf8;
  5. my $X_BOX_CHR = chr(0x2573);
  6. my $GRAY_BOX_CHR = chr(0x2591);
  7. my $ALT_US = chr(0x2017);
  8. my $ALT_PIPE = chr(0x2016);
  9. my $SHADE_1_CHR = $GRAY_BOX_CHR;
  10. my $SHADE_2_CHR = $X_BOX_CHR;
  11. my $PAD_AXIS = " ";
  12. my $PAD_EVEN = " ";
  13. my $PAD_ODD = " ";
  14. my $HEX_1 = '-/--\-';
  15. my $HEX_2 = '| __ |';
  16. my $HEX_3 = '| |';
  17. my $HEX_S1_1 = '-/--\-';
  18. my $HEX_S1_2 = sprintf("|%s%s%s%s|", $SHADE_1_CHR, $SHADE_1_CHR, $SHADE_1_CHR, $SHADE_1_CHR);
  19. my $HEX_S1_3 = sprintf("|%s%s%s%s|", $SHADE_1_CHR, $SHADE_1_CHR, $SHADE_1_CHR, $SHADE_1_CHR);
  20. my $HEX_S2_1 = '-/--\-';
  21. my $HEX_S2_2 = sprintf("|%s%s%s%s|", $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_2_CHR);
  22. my $HEX_S2_3 = sprintf("|%s%s%s%s|", $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_2_CHR);
  23. my $HEX_S3_1 = '-/--\-';
  24. my $HEX_S3_2 = sprintf("|%s%s%s%s|", $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_1_CHR, $SHADE_1_CHR);
  25. my $HEX_S3_3 = sprintf("|%s%s%s%s|", $SHADE_2_CHR, $SHADE_2_CHR, $SHADE_1_CHR, $SHADE_1_CHR);
  26. my $HL_1 = "|____ ";
  27. my $HL_2 = $ALT_PIPE.$ALT_US.$ALT_US.$ALT_US.$ALT_US." ";
  28. my $SUB_CHR = "_";
  29. my $ALT_SUB_CHR = "=";
  30. my $BLANK_CELL_CHR = $ALT_US;
  31. my $PREF_1 = substr($HEX_1, -2, 2);
  32. my $PREF_2 = ' ';
  33. my $PREF_3 = ' ';
  34. my $TRAIL_1 = substr($HEX_1, 0, 1);
  35. my $TRAIL_2 = substr($HEX_1, -1, 1);
  36. my $TRAIL_3 = substr($HEX_1, 0, 2);
  37. # Pieces
  38. my $MG_A = "A"; # Apprentice
  39. my $MG_I = "I"; # Iron Maiden
  40. my $MG_N = "N"; # Nekomata
  41. my $MG_IT = "It"; # Ittan-Momen
  42. my $MG_H = "H"; # Harpy
  43. my $MG_S = "S"; # Slime
  44. my $MG_RC = "Rc"; # Redcap
  45. my $MG_HS = "Hs"; # Holstaur
  46. my $MG_RO = "Ro"; # Red Oni
  47. my $MG_B = "B"; # Blue Oni
  48. my $MG_P = "P"; # Preistess
  49. my $MG_IM = "Im"; # Imp
  50. my $MG_F = "F"; # False Angel
  51. my $MG_QS = "Qs"; # Queen slime
  52. my $MG_AU = "Au"; # Automaton
  53. my $MG_SY = "Sy"; # Sylph
  54. my $MG_Q = "Q"; # Queen
  55. # Players
  56. my $P1 = "P1";
  57. my $P2 = "P2";
  58. my $DIV = ":";
  59. my %AXIS = (
  60. "A" => 1,
  61. "B" => 2,
  62. "C" => 3,
  63. "D" => 4,
  64. "E" => 5,
  65. "F" => 6,
  66. "G" => 7,
  67. "H" => 8,
  68. "I" => 9,
  69. "J" => 10,
  70. "K" => 11
  71. );
  72. my %AXIS_RV = map {
  73. $AXIS{$_} => $_
  74. } keys(%AXIS);
  75. #
  76. # /--\ +1
  77. # | __ |
  78. # | |+1
  79. # \--/
  80. # -1 -1
  81. #
  82. # ODD
  83. # UP: 0, +1
  84. # DOWN: -1, -1
  85. # EVEN
  86. # UP: +1, +1
  87. # DOWN: +0, -1
  88. # [Row move, Diag move]
  89. my %MOVE = (
  90. $MG_A => [
  91. [-1, 1], [ 0, 1]
  92. ],
  93. $MG_Q => [
  94. [ 0, 1], [ 1, 0], [-1, 0], [ 0,-1],
  95. ],
  96. $MG_I => [
  97. [0,0]
  98. ],
  99. $MG_IT => [
  100. [ 0,-1], [ 1,-1]
  101. ],
  102. $MG_N => [
  103. [-1, 2],
  104. [ 1,-2]
  105. ],
  106. $MG_H => [
  107. [-3, 2], [-1,-2], [ 1, 2], [ 3,-2]
  108. ],
  109. $MG_RC => [
  110. [-1, 0], [-2, 0], [ 1, 0], [ 2, 0]
  111. ],
  112. $MG_S => [
  113. [-1, 1], [-2, 2], [ 0, 1], [ 0, 2],
  114. [ 0,-1], [ 0,-2], [ 1,-1], [ 2,-2],
  115. ],
  116. $MG_HS => [
  117. [-3, 1], [-2,-1], [ 2, 1], [ 3,-1]
  118. ],
  119. $MG_RO => [
  120. [-1, 1], [ 0, 1], [ 1, 1], [ 1,-2]
  121. ],
  122. $MG_B => [
  123. [-1, 1], [ 0, 1], [ 0,-2], [ 2,-2]
  124. ],
  125. $MG_P => [
  126. [-4, 2], [-2, 1], [-2,-2], [-1,-1],
  127. [ 1, 1], [ 2,-1], [ 2, 2], [ 4,-2]
  128. ],
  129. $MG_IM => [
  130. [-2, 1], [-1,-1], [-1, 2], [ 1, 1],
  131. [ 1,-1], [ 2,-1]
  132. ],
  133. $MG_F => [
  134. [-1, 0], [-1, 1], [ 0, 1], [ 0,-1],
  135. [ 1, 0], [ 1,-1]
  136. ],
  137. $MG_QS => [
  138. [-3, 3], [-2, 2], [-2, 0], [-1, 0],
  139. [-1, 1], [ 0, 1], [ 0, 2], [ 0, 3],
  140. [ 0,-1], [ 0,-2], [ 0,-3], [ 1, 0],
  141. [ 1,-1], [ 2, 0], [ 2,-2], [ 3,-3],
  142. ],
  143. $MG_AU => [
  144. [-1, 0], [-1, 1], [ 0, 1], [ 0,-1],
  145. [ 1, 0], [ 1,-1]
  146. ],
  147. $MG_SY => [
  148. [-4, 4], [-3, 3], [-3, 2], [-1,-2],
  149. [ 0,-4], [ 0,-3], [ 0, 3], [ 0, 4],
  150. [ 1, 2], [ 3,-2], [ 3,-3], [ 4,-4]
  151. ]
  152. );
  153. my %BLOCK = (
  154. );
  155. # Special cells
  156. my $EMPTY_CELL = "_";
  157. # Shade enum
  158. my $SHADE_3_ENUM = 3;
  159. my $SHADE_2_ENUM = 2;
  160. my $SHADE_1_ENUM = 1;
  161. my $EMPTY_ENUM = 0;
  162. my %SHADE = (
  163. $SHADE_1_ENUM => [$HEX_S1_1, $HEX_S1_2, $HEX_S1_3],
  164. $SHADE_2_ENUM => [$HEX_S2_1, $HEX_S2_2, $HEX_S2_3],
  165. $SHADE_3_ENUM => [$HEX_S3_1, $HEX_S3_2, $HEX_S3_3],
  166. $EMPTY_ENUM => [$HEX_1, $HEX_2, $HEX_3]
  167. );
  168. my $ERR_C_1 = -1;
  169. my %sc = ();
  170. my %board;
  171. # Display pieces
  172. #==================================================
  173. # I hate this function
  174. sub f_1($$$){
  175. my $p = shift;
  176. my $c0 = shift;
  177. my $hex = shift;
  178. my $s0;
  179. $p =~ s/($P1|$P2)$DIV//;
  180. $p .= "_" if length($p) < 2;
  181. $s0 = $hex;
  182. $s0 =~ s/$SUB_CHR{1,2}/$p/;
  183. unless($p eq "__"){
  184. substr($s0, 2,2) = $p;
  185. }
  186. if($c0 % 2 == 0){
  187. $s0 =~ s/$SUB_CHR/$BLANK_CELL_CHR/g;
  188. }
  189. return $s0;
  190. }
  191. # Display shade
  192. #==================================================
  193. sub shade_cell($$){
  194. my $not = shift;
  195. my $en = shift;
  196. $sc{$not} = $en unless (grep /^$not$/, keys(%sc));
  197. }
  198. sub shade_t($){
  199. my $not = shift;
  200. return $sc{$not} if (grep /^$not$/, keys(%sc));
  201. return $EMPTY_ENUM;
  202. }
  203. sub get_shade_chr($){
  204. my $en = shift;
  205. return $SHADE{$en};
  206. }
  207. sub shade_lookup($$){
  208. my ($r0, $c0) = (shift, shift);
  209. my $not;
  210. $not = cell_index_rev($c0, $r0);
  211. return get_shade_chr(shade_t($not));
  212. }
  213. # Display lines
  214. #==================================================
  215. sub disp_1($$){
  216. my ($r0, $c0) = (shift, shift);
  217. my $hex;
  218. $hex = shade_lookup($r0, $c0)->[0];
  219. return $hex;
  220. # return $HEX_1;
  221. }
  222. sub disp_2($$){
  223. my ($r0, $c0) = (shift, shift);
  224. my $p;
  225. my $not;
  226. my $hex;
  227. $hex = shade_lookup($r0, $c0)->[1];
  228. $not = cell_index_rev($c0, $r0);
  229. $p = $board{$not};
  230. return f_1($p, $c0, $hex);
  231. # my ($r0, $c0) = (shift, shift);
  232. # return $HEX_2;
  233. }
  234. sub disp_3($$){
  235. my ($r0, $c0) = (shift, shift);
  236. my $hex;
  237. $hex = shade_lookup($r0, $c0)->[2];
  238. return $hex;
  239. }
  240. sub disp_y_axis($$$$){
  241. my ($s0, $s1, $s2, $r0) = @_;
  242. my $n0;
  243. $n0 = length($r0);
  244. $s0 = $r0 % 2 == 1 ? $PAD_ODD : $PAD_EVEN.$PREF_1;
  245. $s1 = $r0 % 2 == 1 ? $PAD_ODD : $PAD_EVEN.$PREF_2;
  246. $s1 =~ s/^ {$n0}/$r0/;
  247. $s2 = $r0 % 2 == 1 ? $PAD_ODD : $PAD_EVEN.$PREF_3;
  248. return ($s0, $s1, $s2);
  249. }
  250. # Display cell
  251. #==================================================
  252. sub add_cell($$$$$){
  253. my ($s0, $s1, $s2, $r0, $c0) = @_;
  254. $s0 .= disp_1($r0, $c0);
  255. $s1 .= disp_2($r0, $c0);
  256. $s2 .= disp_3($r0, $c0);
  257. return ($s0, $s1, $s2);
  258. }
  259. sub disp_0($){
  260. my $r0;
  261. my ($s0, $s1, $s2);
  262. $r0 = shift;
  263. # Append the y axis
  264. ($s0, $s1, $s2) = disp_y_axis($s0, $s1, $s2, $r0);
  265. # Iterate through the cells
  266. for my $i (1..11){
  267. ($s0, $s1, $s2) = add_cell($s0, $s1, $s2, $r0, $i);
  268. }
  269. return $s0."\n".$s1."\n".$s2."\n";
  270. }
  271. # Display row
  272. #==================================================
  273. sub disp_row($){
  274. my $row;
  275. my $r0;
  276. $r0 = shift;
  277. $row = disp_0($r0);
  278. # Handle trailing characters
  279. if($r0 % 2 == 1){
  280. # Trim the start of the first line
  281. $row =~ s/$TRAIL_1/ /;
  282. # Trim the end of the first line
  283. $row =~ s/\n/$TRAIL_3\n/
  284. }else{
  285. # Trim the end of the last line
  286. $row =~ s/$TRAIL_2\n/ \n/m;
  287. }
  288. return $row;
  289. }
  290. sub disp_trailing_row(){
  291. my $s0;
  292. my $s1;
  293. $s0 = $PAD_EVEN.$PREF_1;
  294. # Clear some chars from
  295. # our hex patterns
  296. $s1 = $HEX_1;
  297. substr($s1, 2,2) = " ";
  298. # Append hex pattern to
  299. # trailing row
  300. for my $i (1..11){
  301. $s0 .= $s1;
  302. }
  303. # Clear one last pair of
  304. # chrs
  305. substr($s0, -2,2) = " ";
  306. $s0 .= "\n";
  307. return $s0;
  308. }
  309. sub disp_x_axis(){
  310. my $s0;
  311. $s0 = $PAD_AXIS;
  312. $s0 .= join(
  313. $PAD_AXIS, map{
  314. $AXIS_RV{$_}
  315. }(1..11));
  316. $s0 .= "\n";
  317. $s0 .= $PAD_AXIS;
  318. $s0 .= join(
  319. "", map{
  320. $_ % 2 == 1 ? $HL_1 : $HL_2
  321. }(1..11));
  322. $s0 .= "\n";
  323. return $s0
  324. }
  325. # Display board
  326. #==================================================
  327. sub disp_board(){
  328. my $b;
  329. for my $i (map {11-$_+1} (1..11)) {
  330. $b .= disp_row($i);
  331. }
  332. $b .= disp_trailing_row();
  333. $b .= disp_x_axis();
  334. $b =~ s/$TRAIL_1$TRAIL_3\n/\n/m;
  335. for my $i (1..11-1){
  336. substr($b, 2+6*$i, 2) = " ";
  337. }
  338. printf("%s", $b);
  339. }
  340. # Caclulate movement
  341. #==================================================
  342. sub cell_pi($){
  343. my $not = shift;
  344. return (split(/$DIV/, $board{$not}))[1]
  345. }
  346. sub cell_pl($){
  347. my $not = shift;
  348. return (split(/$DIV/, $board{$not}))[0]
  349. }
  350. sub cell_index_rev($$){
  351. return $AXIS_RV{$_[0]}.$_[1];
  352. }
  353. sub cell_index($){
  354. my ($chr1, $chr2) = (split(//, $_[0]));
  355. return ($AXIS{$chr1}, $chr2);
  356. }
  357. sub apply_shift($$){
  358. my ($not, $my_shift) = (shift, shift);
  359. my $r0;
  360. my $c0;
  361. ($r0, $c0) = cell_index($not);
  362. $r0 += $my_shift->[0];
  363. $c0 += $my_shift->[1];
  364. # die "cell index [$r0, $c0]: out of bound exception" if $r0 > 11 or $c0 > 11 or $r0 < 1 or $c0 < 1;
  365. return $ERR_C_1 if $r0 > 11 or $c0 > 11 or $r0 < 1 or $c0 < 1;
  366. return cell_index_rev($r0, $c0);
  367. }
  368. sub calc_move($$){
  369. my ($not, $mov) = (shift, shift);
  370. my $r0;
  371. my $c0;
  372. my $x_shift;
  373. my $y_shift;
  374. my $n0;
  375. my $ni;
  376. ($c0, $r0) = cell_index($not);
  377. # get y_shift
  378. $y_shift = $mov->[1];
  379. # get x_shift
  380. $x_shift = $mov->[0];
  381. # make x_shift adjustment
  382. $ni = $y_shift != 0 ? ($y_shift / abs($y_shift)) : 1;
  383. # $ni *= -1;
  384. # moving down: $ni == -1 want value of 0
  385. # moving up: $ni == +1 want value of 1
  386. $n0 = ($r0 + (($ni + 1)/2) ) % 2;
  387. $x_shift += int(($y_shift + ($n0 * $ni)) / 2);
  388. return apply_shift($not, [$x_shift, $y_shift]);
  389. }
  390. sub shade_move($){
  391. my $not = shift;
  392. my $c1;
  393. my @mv1;
  394. my @mv2;
  395. $c1 = cell_pi($not);
  396. @mv1 = @{$MOVE{$c1}};
  397. @mv2 = grep {
  398. $_ ne $ERR_C_1
  399. } map {
  400. calc_move($not, $_)
  401. } @mv1;
  402. for my $mv (@mv2){
  403. shade_cell($mv, $SHADE_1_ENUM);
  404. }
  405. }
  406. # Main starts here!
  407. binmode(STDOUT, ":utf8");
  408. # Creates a hash of the the form
  409. # $board{cell_notation} = piece_enum
  410. %board = map {
  411. $_ => $EMPTY_CELL
  412. } map {
  413. my $l0;
  414. $l0 = $_;
  415. (map {$l0.$_} ("1".."11"))
  416. } map {
  417. $AXIS_RV{$_}
  418. }(1...11);
  419. # my $m1 = "F7";
  420. my $m1 = "A1";
  421. my $c1 = $MG_QS;
  422. $board{$m1} = $P1.$DIV.$c1;
  423. shade_move($m1);
  424. disp_board();