console-shogi 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +21 -0
  4. data/images/active/gote/fu.png +0 -0
  5. data/images/active/gote/gin.png +0 -0
  6. data/images/active/gote/gyoku.png +0 -0
  7. data/images/active/gote/hisha.png +0 -0
  8. data/images/active/gote/kaku.png +0 -0
  9. data/images/active/gote/keima.png +0 -0
  10. data/images/active/gote/kin.png +0 -0
  11. data/images/active/gote/kyosha.png +0 -0
  12. data/images/active/gote/narigin.png +0 -0
  13. data/images/active/gote/narikei.png +0 -0
  14. data/images/active/gote/narikyo.png +0 -0
  15. data/images/active/gote/ohsho.png +0 -0
  16. data/images/active/gote/ryu.png +0 -0
  17. data/images/active/gote/tokin.png +0 -0
  18. data/images/active/gote/uma.png +0 -0
  19. data/images/active/nonepiece.png +0 -0
  20. data/images/active/sente/fu.png +0 -0
  21. data/images/active/sente/gin.png +0 -0
  22. data/images/active/sente/gyoku.png +0 -0
  23. data/images/active/sente/hisha.png +0 -0
  24. data/images/active/sente/kaku.png +0 -0
  25. data/images/active/sente/keima.png +0 -0
  26. data/images/active/sente/kin.png +0 -0
  27. data/images/active/sente/kyosha.png +0 -0
  28. data/images/active/sente/narigin.png +0 -0
  29. data/images/active/sente/narikei.png +0 -0
  30. data/images/active/sente/narikyo.png +0 -0
  31. data/images/active/sente/ohsho.png +0 -0
  32. data/images/active/sente/ryu.png +0 -0
  33. data/images/active/sente/tokin.png +0 -0
  34. data/images/active/sente/uma.png +0 -0
  35. data/images/focused/gote/fu.png +0 -0
  36. data/images/focused/gote/gin.png +0 -0
  37. data/images/focused/gote/gyoku.png +0 -0
  38. data/images/focused/gote/hisha.png +0 -0
  39. data/images/focused/gote/kaku.png +0 -0
  40. data/images/focused/gote/keima.png +0 -0
  41. data/images/focused/gote/kin.png +0 -0
  42. data/images/focused/gote/kyosha.png +0 -0
  43. data/images/focused/gote/narigin.png +0 -0
  44. data/images/focused/gote/narikei.png +0 -0
  45. data/images/focused/gote/narikyo.png +0 -0
  46. data/images/focused/gote/ohsho.png +0 -0
  47. data/images/focused/gote/ryu.png +0 -0
  48. data/images/focused/gote/tokin.png +0 -0
  49. data/images/focused/gote/uma.png +0 -0
  50. data/images/focused/nonepiece.png +0 -0
  51. data/images/focused/sente/fu.png +0 -0
  52. data/images/focused/sente/gin.png +0 -0
  53. data/images/focused/sente/gyoku.png +0 -0
  54. data/images/focused/sente/hisha.png +0 -0
  55. data/images/focused/sente/kaku.png +0 -0
  56. data/images/focused/sente/keima.png +0 -0
  57. data/images/focused/sente/kin.png +0 -0
  58. data/images/focused/sente/kyosha.png +0 -0
  59. data/images/focused/sente/narigin.png +0 -0
  60. data/images/focused/sente/narikei.png +0 -0
  61. data/images/focused/sente/narikyo.png +0 -0
  62. data/images/focused/sente/ohsho.png +0 -0
  63. data/images/focused/sente/ryu.png +0 -0
  64. data/images/focused/sente/tokin.png +0 -0
  65. data/images/focused/sente/uma.png +0 -0
  66. data/images/history_button/left.png +0 -0
  67. data/images/history_button/play.png +0 -0
  68. data/images/history_button/right.png +0 -0
  69. data/lib/console_shogi/board.rb +5 -9
  70. data/lib/console_shogi/game.rb +98 -24
  71. data/lib/console_shogi/game_history.rb +60 -0
  72. data/lib/console_shogi/grid_position.rb +6 -0
  73. data/lib/console_shogi/komadai.rb +22 -18
  74. data/lib/console_shogi/new_board_builder.rb +41 -41
  75. data/lib/console_shogi/piece.rb +39 -23
  76. data/lib/console_shogi/piece_movement_checker.rb +46 -0
  77. data/lib/console_shogi/piece_mover.rb +21 -68
  78. data/lib/console_shogi/piece_mover_from_board.rb +57 -0
  79. data/lib/console_shogi/piece_mover_from_komadai.rb +51 -0
  80. data/lib/console_shogi/player.rb +5 -13
  81. data/lib/console_shogi/teban.rb +8 -0
  82. data/lib/console_shogi/terminal/cursor.rb +74 -0
  83. data/lib/console_shogi/terminal/display_area.rb +122 -0
  84. data/lib/console_shogi/terminal/operator.rb +249 -0
  85. data/lib/console_shogi/version.rb +1 -1
  86. data/lib/console_shogi.rb +11 -3
  87. metadata +110 -39
  88. data/images/gote/nonepiece.png +0 -0
  89. data/lib/console_shogi/piece_mover_on_komadai.rb +0 -65
  90. data/lib/console_shogi/terminal_operator.rb +0 -211
  91. /data/images/{gote/shori.png → gote_shori.png} +0 -0
  92. /data/images/{gote → nomal/gote}/fu.png +0 -0
  93. /data/images/{gote → nomal/gote}/gin.png +0 -0
  94. /data/images/{gote → nomal/gote}/gyoku.png +0 -0
  95. /data/images/{gote → nomal/gote}/hisha.png +0 -0
  96. /data/images/{gote → nomal/gote}/kaku.png +0 -0
  97. /data/images/{gote → nomal/gote}/keima.png +0 -0
  98. /data/images/{gote → nomal/gote}/kin.png +0 -0
  99. /data/images/{gote → nomal/gote}/kyosha.png +0 -0
  100. /data/images/{gote → nomal/gote}/narigin.png +0 -0
  101. /data/images/{gote → nomal/gote}/narikei.png +0 -0
  102. /data/images/{gote → nomal/gote}/narikyo.png +0 -0
  103. /data/images/{gote → nomal/gote}/ohsho.png +0 -0
  104. /data/images/{gote → nomal/gote}/ryu.png +0 -0
  105. /data/images/{gote → nomal/gote}/tokin.png +0 -0
  106. /data/images/{gote → nomal/gote}/uma.png +0 -0
  107. /data/images/{sente → nomal}/nonepiece.png +0 -0
  108. /data/images/{sente → nomal/sente}/fu.png +0 -0
  109. /data/images/{sente → nomal/sente}/gin.png +0 -0
  110. /data/images/{sente → nomal/sente}/gyoku.png +0 -0
  111. /data/images/{sente → nomal/sente}/hisha.png +0 -0
  112. /data/images/{sente → nomal/sente}/kaku.png +0 -0
  113. /data/images/{sente → nomal/sente}/keima.png +0 -0
  114. /data/images/{sente → nomal/sente}/kin.png +0 -0
  115. /data/images/{sente → nomal/sente}/kyosha.png +0 -0
  116. /data/images/{sente → nomal/sente}/narigin.png +0 -0
  117. /data/images/{sente → nomal/sente}/narikei.png +0 -0
  118. /data/images/{sente → nomal/sente}/narikyo.png +0 -0
  119. /data/images/{sente → nomal/sente}/ohsho.png +0 -0
  120. /data/images/{sente → nomal/sente}/ryu.png +0 -0
  121. /data/images/{sente → nomal/sente}/tokin.png +0 -0
  122. /data/images/{sente → nomal/sente}/uma.png +0 -0
  123. /data/images/{sente/shori.png → sente_shori.png} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44d942e8d1dbffb2bfae8c29d4154ea7b148484eaaa976d38e51a7c20779ea2a
4
- data.tar.gz: 0c6fb8f9f65961284023eb6b5312a0df1240b1c3af0b3aa4d8de08e5640dbeea
3
+ metadata.gz: bee75f732b33e177d7b2c15939508663a391bc2d593692273a1a1f47ae654543
4
+ data.tar.gz: d78846e34e4afbeea51dfd4558e9c076aee6625c6ad43129fb01cff9c36d41a7
5
5
  SHA512:
6
- metadata.gz: a1c70713c4b868e5308f3643128b8a34579956cb5ba7aadf0170ed3142f2416efa40a4a69bb7f3939324a133c348a578a3c26a77ded659f59d6133cbe653a4a3
7
- data.tar.gz: 95c8b78de329d1e66ec81f3c25a2efe281ad89733027b778d00c5b7ac59d726229634d2e5ca083d8783d7827700a70858abc2fec506000912cd2548f5ffb7079
6
+ metadata.gz: 2b0aeed55b0fcaedbe879703c3d7e20fc38a6fa5e2a0617d627ff6f873766aff6f4963dc117f032b64911021a46888ebb2229ed6d4a4231bd91d9ecbcf5a868a
7
+ data.tar.gz: 926eeda23c9e565c45ec8290c0ecf5ca607adfaea24c29f1a95c858f952af948b7e371ae8408f32512c3acd720bb3af5873dce62aa8ff435e0801d1cf2b1cc35
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- console-shogi (0.1.0)
4
+ console-shogi (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1 +1,22 @@
1
1
  # console-shogi
2
+
3
+ You can play the Shogi in your terminal ☗☖
4
+
5
+ Maybe, you’ll hard to see if you don’t increase the font size of the terminal.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ gem install console-shogi
11
+ ```
12
+
13
+ ## Start Game
14
+
15
+ ```
16
+ console-shogi
17
+ ```
18
+
19
+ ## Commands
20
+
21
+ * move a cursor : `direction keys` ←↓↑→
22
+ * select and drop a piece : `Enter`
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -12,6 +12,10 @@ module ConsoleShogi
12
12
  @pieces
13
13
  end
14
14
 
15
+ def deep_copy
16
+ Board.new(pieces: pieces.map(&:dup).to_a)
17
+ end
18
+
15
19
  def within_range?(x:, y:)
16
20
  case {x: x, y: y}
17
21
  in x: 0..8, y: 0..8
@@ -34,16 +38,8 @@ module ConsoleShogi
34
38
  end
35
39
 
36
40
  def move_piece!(from:, to:)
37
- from_piece = @pieces[from[:y], from[:x]]
41
+ @pieces[to[:y], to[:x]] = @pieces[from[:y], from[:x]]
38
42
  @pieces[from[:y], from[:x]] = NonePiece.new
39
-
40
- to_piece = @pieces[to[:y], to[:x]]
41
-
42
- # NOTE 駒を取る、リファクタする
43
- # from_piece.player で player 取るの違和感
44
- from_piece.player.capture_piece!(to_piece) unless to_piece.none?
45
-
46
- @pieces[to[:y], to[:x]] = from_piece
47
43
  end
48
44
 
49
45
  private
@@ -5,15 +5,25 @@ require 'io/console'
5
5
  module ConsoleShogi
6
6
  class Game
7
7
  def initialize
8
- @sente_player = Player.new(teban: Player::Teban::SENTE)
9
- @gote_player = Player.new(teban: Player::Teban::GOTE)
10
- @board = NewBoardBuilder.build(sente_player: @sente_player, gote_player: @gote_player)
8
+ @sente_player = Player.new(teban: Teban::SENTE)
9
+ @gote_player = Player.new(teban: Teban::GOTE)
10
+ @board = NewBoardBuilder.build
11
+
12
+ @selected_cursor = nil
13
+ @teban_player = @sente_player
14
+
15
+ @game_histories = [{board: board.deep_copy, sente_komadai: sente_player.komadai.deep_copy, gote_komadai: gote_player.komadai.deep_copy}]
16
+
17
+ Terminal::Operator.clear_scrren
18
+ Terminal::Operator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
19
+ Terminal::Operator.print_history_button
20
+ Terminal::Operator.print_teban(teban_player.teban)
11
21
  end
12
22
 
13
23
  def start
14
- TerminalOperator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
15
-
16
24
  while key = STDIN.getch
25
+ cursor = Terminal::Operator.cursor
26
+
17
27
  # NOTE Ctrl-C を押したら終了
18
28
  if key == "\C-c"
19
29
  exit
@@ -21,30 +31,54 @@ module ConsoleShogi
21
31
  elsif key == "\e" && STDIN.getch == "["
22
32
  key = STDIN.getch
23
33
 
24
- TerminalOperator.move_cursor(key)
34
+ @last_cursor_on_grid = cursor.dup unless cursor.grid_position.location.outside?
35
+
36
+ cursor.move(key)
37
+
38
+ # TODO 選択したピースは色を変えないようにしている。状態の持ち方を見直したい
39
+ deactive_piece(last_cursor_on_grid) if selected_cursor&.grid_position != last_cursor_on_grid.grid_position
40
+ focus_piece(cursor) if selected_cursor&.grid_position != cursor.grid_position
25
41
  # NOTE Enter を押したら駒を移動
26
42
  elsif key == "\r"
27
- # TODO このまま Hash にするかは要検討
28
- index = TerminalOperator.squares_index
29
-
30
- case index[:location]
31
- when :board
32
- PieceMover.new(board: board, from: index).move!
33
- when :sente_komadai
34
- PieceMoverOnKomadai.new(board: board, komadai: sente_player.komadai, from: index).drop!
35
- when :gote_komadai
36
- PieceMoverOnKomadai.new(board: board, komadai: gote_player.komadai, from: index).drop!
43
+ if cursor.grid_position.location.history?
44
+ GameHistory.new(game_histories: game_histories).start
45
+
46
+ next
37
47
  end
38
48
 
39
- # TODO 描写に時間がかかるので、差分のみ表示できる様にする
40
- TerminalOperator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
49
+ if selected_cursor.nil?
50
+ # TODO PieceMover can_move? と分散してしまっている気もする
51
+ next if cursor.grid_position.location.outside?
52
+
53
+ active_piece(cursor)
41
54
 
42
- # TODO ひとまず動く物を作った。リファクタリングする
43
- [sente_player, gote_player].each do |player|
44
- next unless player.win?
55
+ @selected_cursor = cursor.dup
56
+ else
57
+ next unless cursor.grid_position.location.board?
45
58
 
46
- TerminalOperator.print_winner(player.teban)
47
- exit
59
+ piece_mover = PieceMover.build(board: board, player: teban_player, from_cursor: selected_cursor, to_cursor: cursor)
60
+
61
+ piece_mover.move!
62
+
63
+ if piece_mover.moved_piece?
64
+ Terminal::Operator.print_diff_board(previous_board: game_histories.last[:board], board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
65
+
66
+ @game_histories << {board: board.deep_copy, sente_komadai: sente_player.komadai.deep_copy, gote_komadai: gote_player.komadai.deep_copy}
67
+
68
+ if teban_player.win?
69
+ Terminal::Operator.print_winner(teban_player)
70
+
71
+ exit
72
+ else
73
+ change_teban!
74
+
75
+ Terminal::Operator.print_teban(teban_player.teban)
76
+ end
77
+ else
78
+ deactive_piece(selected_cursor)
79
+ end
80
+
81
+ @selected_cursor = nil
48
82
  end
49
83
  end
50
84
  end
@@ -52,6 +86,46 @@ module ConsoleShogi
52
86
 
53
87
  private
54
88
 
55
- attr_reader :board, :sente_player, :gote_player
89
+ attr_reader :board, :sente_player, :gote_player, :selected_cursor, :teban_player, :game_histories, :last_cursor_on_grid
90
+
91
+ def change_teban!
92
+ @teban_player = teban_player == sente_player ? gote_player : sente_player
93
+ end
94
+
95
+ # TODO ここらへんイケてないのでリファクタしたい
96
+ def active_piece(cursor)
97
+ location = fetch_piece_location_object(cursor.grid_position.location)
98
+
99
+ return if location.nil?
100
+
101
+ Terminal::Operator.active_piece(board: location, cursor: cursor)
102
+ end
103
+
104
+ def deactive_piece(cursor)
105
+ location = fetch_piece_location_object(cursor.grid_position.location)
106
+
107
+ return if location.nil?
108
+
109
+ Terminal::Operator.deactive_piece(board: location, previous_cursor: cursor)
110
+ end
111
+
112
+ def focus_piece(cursor)
113
+ location = fetch_piece_location_object(cursor.grid_position.location)
114
+
115
+ return if location.nil?
116
+
117
+ Terminal::Operator.focus_piece(board: location, cursor: cursor)
118
+ end
119
+
120
+ def fetch_piece_location_object(cursor_location)
121
+ case cursor_location.name
122
+ when :board
123
+ board
124
+ when :sente_komadai
125
+ sente_player.komadai
126
+ when :gote_komadai
127
+ gote_player.komadai
128
+ end
129
+ end
56
130
  end
57
131
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+
5
+ module ConsoleShogi
6
+ class GameHistory
7
+ def initialize(game_histories:)
8
+ @game_histories = game_histories
9
+ # TODO game_count という名前は分かりづらいので変える
10
+ @game_count = game_histories.count
11
+
12
+ Terminal::Operator.print_start_history
13
+ end
14
+
15
+ def start
16
+ while key = STDIN.getch
17
+ cursor = Terminal::Operator.cursor
18
+
19
+ # NOTE Ctrl-C を押したら終了
20
+ if key == "\C-c"
21
+ exit
22
+ # NOTE 矢印キーを押したらカーソルを移動
23
+ elsif key == "\e" && STDIN.getch == "["
24
+ key = STDIN.getch
25
+
26
+ cursor.move(key)
27
+ elsif key == "\r"
28
+ location = cursor.grid_position.location
29
+
30
+ next unless location.history?
31
+
32
+ if location.resume?
33
+ Terminal::Operator.clear_scrren
34
+
35
+ Terminal::Operator.print_board(board: game_histories.last[:board], sente_komadai: game_histories.last[:sente_komadai], gote_komadai: game_histories.last[:gote_komadai])
36
+ Terminal::Operator.print_history_button
37
+
38
+ return
39
+ elsif location.back?
40
+ next if game_count == 1
41
+
42
+ @game_count -= 1
43
+
44
+ Terminal::Operator.print_board(board: game_histories[game_count - 1][:board], sente_komadai: game_histories[game_count - 1][:sente_komadai], gote_komadai: game_histories[game_count - 1][:gote_komadai])
45
+ elsif location.forward?
46
+ next if game_count == game_histories.count
47
+
48
+ @game_count += 1
49
+
50
+ Terminal::Operator.print_board(board: game_histories[game_count - 1][:board], sente_komadai: game_histories[game_count - 1][:sente_komadai], gote_komadai: game_histories[game_count - 1][:gote_komadai])
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :game_histories, :game_count
59
+ end
60
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleShogi
4
+ class GridPosition < Struct.new(:x, :y, :location)
5
+ end
6
+ end
@@ -8,16 +8,12 @@ module ConsoleShogi
8
8
 
9
9
  attr_reader :pieces
10
10
 
11
- def initialize
12
- @pieces = empty_pieces
11
+ def initialize(pieces: empty_pieces)
12
+ @pieces = Matrix.rows(pieces)
13
13
  end
14
14
 
15
- def expand!
16
- @pieces = pieces.hstack(empty_pieces)
17
- end
18
-
19
- def have_space?
20
- pieces.any?(&:none?)
15
+ def deep_copy
16
+ Komadai.new(pieces: pieces.map(&:dup).to_a)
21
17
  end
22
18
 
23
19
  def fetch_piece(x:, y:)
@@ -25,35 +21,43 @@ module ConsoleShogi
25
21
  end
26
22
 
27
23
  def put(piece:)
24
+ expand! unless have_space?
25
+
28
26
  index = pieces.index(&:none?)
29
27
 
30
- raise NoSpaceError if index.nil?
28
+ piece = piece.original if piece.promoted?
31
29
 
32
30
  @pieces[*index] = piece
33
31
 
34
32
  @pieces = sort_pieces
35
33
  end
36
34
 
37
- def pick_up_piece!(from:)
38
- @pieces[from[:y], from[:x]] = NonePiece.new
35
+ def pick_up_piece!(x:, y:)
36
+ @pieces[y, x] = NonePiece.new
39
37
  @pieces = sort_pieces
40
38
  end
41
39
 
42
40
  private
43
41
 
42
+ def expand!
43
+ @pieces = pieces.hstack(empty_pieces)
44
+ end
45
+
46
+ def have_space?
47
+ pieces.any?(&:none?)
48
+ end
49
+
44
50
  def sort_pieces
45
51
  column_num = pieces.count / 3
46
52
  Matrix.rows(pieces.sort_by {|p| -p.number }.each_slice(column_num).to_a)
47
53
  end
48
54
 
49
55
  def empty_pieces
50
- Matrix.rows(
51
- [
52
- [NonePiece.new, NonePiece.new, NonePiece.new],
53
- [NonePiece.new, NonePiece.new, NonePiece.new],
54
- [NonePiece.new, NonePiece.new, NonePiece.new]
55
- ]
56
- )
56
+ [
57
+ [NonePiece.new, NonePiece.new, NonePiece.new],
58
+ [NonePiece.new, NonePiece.new, NonePiece.new],
59
+ [NonePiece.new, NonePiece.new, NonePiece.new]
60
+ ]
57
61
  end
58
62
  end
59
63
  end