pacmanrb 0.1.1

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +46 -0
  4. data/app/components/board_component.rb +100 -0
  5. data/app/components/hud_component.rb +15 -0
  6. data/app/controllers/application_controller.rb +24 -0
  7. data/app/controllers/game_controller.rb +68 -0
  8. data/app/controllers/game_over_controller.rb +26 -0
  9. data/app/controllers/home_controller.rb +22 -0
  10. data/app/models/application_record.rb +7 -0
  11. data/app/models/high_score.rb +7 -0
  12. data/app/state/application_state.rb +6 -0
  13. data/app/state/game_over_state.rb +9 -0
  14. data/app/state/game_state.rb +9 -0
  15. data/app/state/home_state.rb +7 -0
  16. data/app/views/game/show_view.rb +32 -0
  17. data/app/views/game_over/show_view.rb +40 -0
  18. data/app/views/home/show_view.rb +42 -0
  19. data/app/views/layouts/application_layout.rb +34 -0
  20. data/config/database.rb +14 -0
  21. data/config/routes.rb +7 -0
  22. data/db/development.sqlite3 +0 -0
  23. data/db/migrate/20260719010011_create_high_scores.rb +11 -0
  24. data/db/schema.rb +20 -0
  25. data/db/seeds.rb +1 -0
  26. data/db/test.sqlite3 +0 -0
  27. data/exe/pacmanrb +7 -0
  28. data/lib/pacman/application.rb +13 -0
  29. data/lib/pacman/arcade/brains.rb +46 -0
  30. data/lib/pacman/arcade/direction.rb +19 -0
  31. data/lib/pacman/arcade/ghost.rb +72 -0
  32. data/lib/pacman/arcade/layouts.rb +28 -0
  33. data/lib/pacman/arcade/maze.rb +67 -0
  34. data/lib/pacman/arcade/mode_schedule.rb +52 -0
  35. data/lib/pacman/arcade/navigator.rb +36 -0
  36. data/lib/pacman/arcade/pellet_field.rb +39 -0
  37. data/lib/pacman/arcade/player.rb +42 -0
  38. data/lib/pacman/arcade/position.rb +15 -0
  39. data/lib/pacman/arcade/scoreboard.rb +43 -0
  40. data/lib/pacman/arcade/spawn.rb +8 -0
  41. data/lib/pacman/arcade/world.rb +169 -0
  42. data/lib/pacman/board_scale.rb +42 -0
  43. data/lib/pacman/sprites.rb +145 -0
  44. data/lib/pacman/version.rb +5 -0
  45. data/lib/pacman.rb +22 -0
  46. metadata +127 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pacman
4
+ # Procedurally drawn actor sprites. A board cell is `scale` character rows by
5
+ # `scale * 2` columns; half-block characters double the vertical resolution,
6
+ # giving a square 2k x 2k pixel grid to draw circles and skirts on.
7
+ class Sprites
8
+ MOUTH_COSINE = 0.75
9
+
10
+ def self.player(scale:, direction:)
11
+ return ["██"] if scale == 1
12
+
13
+ sprite = new(scale)
14
+ sprite.draw { |y, x| sprite.pacman?(y, x, direction) }
15
+ end
16
+
17
+ def self.ghost(scale:)
18
+ return ["██"] if scale == 1
19
+
20
+ sprite = new(scale)
21
+ sprite.draw { |y, x| sprite.ghost_body?(y, x) && !sprite.eye?(y, x) }
22
+ end
23
+
24
+ def self.frightened(scale:)
25
+ return ["▒▒"] if scale == 1
26
+
27
+ sprite = new(scale)
28
+ sprite.draw(texture: "▒") { |y, x| sprite.ghost_body?(y, x) && !sprite.eye?(y, x) }
29
+ end
30
+
31
+ def self.cherry(scale:)
32
+ return ["● "] if scale == 1
33
+
34
+ new(scale).cherry
35
+ end
36
+
37
+ def self.eaten(scale:)
38
+ return ["▀▀"] if scale == 1
39
+
40
+ sprite = new(scale)
41
+ sprite.draw { |y, x| sprite.eye?(y, x) }
42
+ end
43
+
44
+ def initialize(scale)
45
+ @scale = scale
46
+ @pixels = scale * 2
47
+ @center = (@pixels - 1) / 2.0
48
+ @radius = scale - 0.15
49
+ end
50
+
51
+ def draw(texture: nil)
52
+ (0...scale).map do |row|
53
+ (0...pixels).map do |col|
54
+ cell_char(yield(row * 2, col), yield(row * 2 + 1, col), texture)
55
+ end.join
56
+ end
57
+ end
58
+
59
+ def cherry
60
+ body = draw { |y, x| berry?(y, x) }
61
+ add_stems(body)
62
+ end
63
+
64
+ def pacman?(y, x, direction)
65
+ circle?(y, x) && !mouth?(y, x, direction)
66
+ end
67
+
68
+ def ghost_body?(y, x)
69
+ return skirt?(x) && (x - center).abs <= radius if y == pixels - 1
70
+ return circle?(y, x) if y < scale
71
+
72
+ (x - center).abs <= radius
73
+ end
74
+
75
+ def eye?(y, x)
76
+ return false if scale < 2
77
+
78
+ size = [scale / 2, 1].max
79
+ row = (pixels * 0.35).floor
80
+ return false unless (row...(row + size)).cover?(y)
81
+
82
+ left = (pixels * 0.3).floor
83
+ right = (pixels * 0.65).floor
84
+ (left...(left + size)).cover?(x) || (right...(right + size)).cover?(x)
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :scale, :pixels, :center, :radius
90
+
91
+ BERRY_RADIUS = 0.42
92
+ BERRY_ROW = 1.4
93
+ BERRY_COLS = [0.55, 1.45].freeze
94
+
95
+ def berry?(y, x)
96
+ r = scale * BERRY_RADIUS
97
+ BERRY_COLS.any? do |col|
98
+ ((y - (scale * BERRY_ROW))**2) + ((x - (scale * col))**2) <= r**2
99
+ end
100
+ end
101
+
102
+ def add_stems(lines)
103
+ stem_rows = lines.take_while { |line| line.strip.empty? }.length
104
+ (0...stem_rows).each do |row|
105
+ t = (row + 1).to_f / (stem_rows + 1)
106
+ stem_columns(t).each { |col| lines[row][col] = "│" }
107
+ end
108
+ lines
109
+ end
110
+
111
+ def stem_columns(t)
112
+ BERRY_COLS.map { |col| (scale + (((scale * col) - scale) * t)).round }.uniq
113
+ end
114
+
115
+ def cell_char(top, bottom, texture)
116
+ return texture_char(top, bottom, texture) if texture
117
+ return "█" if top && bottom
118
+ return "▀" if top
119
+ return "▄" if bottom
120
+
121
+ " "
122
+ end
123
+
124
+ def texture_char(top, bottom, texture)
125
+ (top || bottom) ? texture : " "
126
+ end
127
+
128
+ def circle?(y, x)
129
+ ((y - center)**2) + ((x - center)**2) <= radius**2
130
+ end
131
+
132
+ def mouth?(y, x, direction)
133
+ vy = y - center
134
+ vx = x - center
135
+ length = Math.sqrt((vy**2) + (vx**2))
136
+ return false if length < radius * 0.15
137
+
138
+ ((vy * direction.dy) + (vx * direction.dx)) / length > MOUTH_COSINE
139
+ end
140
+
141
+ def skirt?(x)
142
+ ((x * 4) / pixels).even?
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pacman
4
+ VERSION = "0.1.1"
5
+ end
data/lib/pacman.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "charming"
4
+ require "zeitwerk"
5
+ require_relative "../config/database"
6
+
7
+
8
+ module Pacman
9
+ end
10
+
11
+ loader = Zeitwerk::Loader.new
12
+ loader.tag = "pacman"
13
+ loader.inflector.inflect("version" => "VERSION")
14
+ loader.push_dir(File.expand_path("pacman", __dir__), namespace: Pacman)
15
+ loader.push_dir(File.expand_path("../app/models", __dir__), namespace: Pacman)
16
+ loader.push_dir(File.expand_path("../app/state", __dir__), namespace: Pacman)
17
+ loader.push_dir(File.expand_path("../app/components", __dir__), namespace: Pacman)
18
+ loader.push_dir(File.expand_path("../app/views", __dir__), namespace: Pacman)
19
+ loader.push_dir(File.expand_path("../app/controllers", __dir__), namespace: Pacman)
20
+ loader.setup
21
+
22
+ require_relative "../config/routes"
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pacmanrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Pando
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: charming
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sqlite3
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: activerecord
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '8.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '8.1'
54
+ email:
55
+ - pandorocks@proton.me
56
+ executables:
57
+ - pacmanrb
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - LICENSE.txt
62
+ - README.md
63
+ - app/components/board_component.rb
64
+ - app/components/hud_component.rb
65
+ - app/controllers/application_controller.rb
66
+ - app/controllers/game_controller.rb
67
+ - app/controllers/game_over_controller.rb
68
+ - app/controllers/home_controller.rb
69
+ - app/models/application_record.rb
70
+ - app/models/high_score.rb
71
+ - app/state/application_state.rb
72
+ - app/state/game_over_state.rb
73
+ - app/state/game_state.rb
74
+ - app/state/home_state.rb
75
+ - app/views/game/show_view.rb
76
+ - app/views/game_over/show_view.rb
77
+ - app/views/home/show_view.rb
78
+ - app/views/layouts/application_layout.rb
79
+ - config/database.rb
80
+ - config/routes.rb
81
+ - db/development.sqlite3
82
+ - db/migrate/20260719010011_create_high_scores.rb
83
+ - db/schema.rb
84
+ - db/seeds.rb
85
+ - db/test.sqlite3
86
+ - exe/pacmanrb
87
+ - lib/pacman.rb
88
+ - lib/pacman/application.rb
89
+ - lib/pacman/arcade/brains.rb
90
+ - lib/pacman/arcade/direction.rb
91
+ - lib/pacman/arcade/ghost.rb
92
+ - lib/pacman/arcade/layouts.rb
93
+ - lib/pacman/arcade/maze.rb
94
+ - lib/pacman/arcade/mode_schedule.rb
95
+ - lib/pacman/arcade/navigator.rb
96
+ - lib/pacman/arcade/pellet_field.rb
97
+ - lib/pacman/arcade/player.rb
98
+ - lib/pacman/arcade/position.rb
99
+ - lib/pacman/arcade/scoreboard.rb
100
+ - lib/pacman/arcade/spawn.rb
101
+ - lib/pacman/arcade/world.rb
102
+ - lib/pacman/board_scale.rb
103
+ - lib/pacman/sprites.rb
104
+ - lib/pacman/version.rb
105
+ homepage: https://charming.sh/docs/examples/pacman/
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ rubygems_mfa_required: 'true'
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 4.0.0
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 4.0.16
125
+ specification_version: 4
126
+ summary: A Charming terminal user interface.
127
+ test_files: []