pwn 0.5.751 → 0.5.752
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.
- checksums.yaml +4 -4
- data/documentation/pwn-ai-Agent.md +7 -3
- data/etc/default_skills/pwn/banner/SKILL.md +1 -1
- data/lib/pwn/banner.rb +21 -8
- data/lib/pwn/plugins/repl/ai_console.rb +4 -4
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/banner_spec.rb +31 -4
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +35 -15
- data/third_party/pwn_rdoc.jsonl +3 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f090409a234eeffe46292971f044bd3f6109da51d03e8fae5909d63aa625808
|
|
4
|
+
data.tar.gz: '082fcd50fde34da2c0bea93a7292a4925fd017e2526e07d6fcf8c9b29bb5db8c'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 612c4b3a4d8f8311e231c2bbcceee5cd61280134157adc9f2132beb438ef65bbb698ed949aca56d19412414ed10c8540a1feab8327aaf14abcc71c33c2b23ccf
|
|
7
|
+
data.tar.gz: '09b64fed3ccd1e064a28b4874005e73549c8fbd7d60017dd6c552cce7bd6b45577dfb9202d7aecc54aa13456a2cdc847ef021850d387139bfa064fdc88fbc7d5'
|
|
@@ -31,7 +31,9 @@ its seeded autonomous flight is decorative, not player-controlled.
|
|
|
31
31
|
Its framed width in terminal cells equals the complete header's height in rows;
|
|
32
32
|
the interior canvas is `(header height - 2)` cells on each side. This is a
|
|
33
33
|
cell-square, not a pixel-square—terminal glyph cells are usually taller than wide.
|
|
34
|
-
|
|
34
|
+
The animation pane has no wordmark; its former label row belongs to gameplay.
|
|
35
|
+
The main `pwn-ai` header title and full-screen static banners are unchanged.
|
|
36
|
+
Falling blocks fill the entire interior, including the
|
|
35
37
|
bottom two quadrants. Each logical block occupies one horizontal half-cell from
|
|
36
38
|
spawn through landing and locking. This deliberately replaces independent
|
|
37
39
|
quarter-cell blocks: a terminal cell has only a foreground and background, so
|
|
@@ -51,11 +53,13 @@ changing the active session selects again. Frames advance at the banner API's
|
|
|
51
53
|
blocks/snake) using monotonic time in the existing render loop—no extra animation
|
|
52
54
|
thread, input reader or provider call. This is decoration, not progress or
|
|
53
55
|
telemetry. The frame keeps the existing border/title theme roles. Artwork comes
|
|
54
|
-
from `PWN::Banner.mini_cells` with dedicated foreground/background curses pairs,
|
|
56
|
+
from `PWN::Banner.mini_cells(branding: false)` with dedicated foreground/background curses pairs,
|
|
55
57
|
not ANSI output or theme overrides. `NO_COLOR` and terminals with too few color
|
|
56
58
|
pairs retain occupied geometry in monochrome (two differently colored occupied
|
|
57
59
|
halves become a full block, not a half-block with its background lost). The `mini_frame` ASCII API
|
|
58
|
-
retains its 60-frame, 0.1-second loops for other callers.
|
|
60
|
+
retains its 60-frame, 0.1-second loops for other callers. Both miniature APIs
|
|
61
|
+
default to their legacy branding; `branding: false` reclaims the label row and
|
|
62
|
+
leaves undersized fallback panes blank. Colored replays contain
|
|
59
63
|
1800 frames (90 seconds for Pong/Asteroids, 180 for blocks/snake), use a local
|
|
60
64
|
per-session seed, and share a bounded six-replay cache. Settings
|
|
61
65
|
wrap in the right-hand region without losing their label colors. A bounded,
|
|
@@ -12,7 +12,7 @@ metadata:
|
|
|
12
12
|
|
|
13
13
|
# PWN::Banner
|
|
14
14
|
|
|
15
|
-
Static banners and pure, PWN-branded retro ASCII/color loops: falling_blocks, snake, pong, and asteroids. mini_frame returns fresh rows on a borderless canvas; use equal width and height (5..16) for complete square artwork. Dimensions clamp to 0..16 (colored blocks: 0..64)
|
|
15
|
+
Static banners and pure, PWN-branded retro ASCII/color loops: falling_blocks, snake, pong, and asteroids. mini_frame returns fresh rows on a borderless canvas; use equal width and height (5..16) for complete square artwork. Dimensions clamp to 0..16 (colored blocks: 0..64). Set branding: false for an unbranded pane with the wordmark row reclaimed for play; tiny panes then stay blank. The caller samples mini_names once per session and advances the explicit frame index every mini_frame_seconds(name:) (0.05 for Pong/Asteroids, 0.1 otherwise). mini_cells replays 1800 seeded gameplay frames (MINI_FRAME_COUNT); mini_frame retains its legacy 60-frame ASCII loop. These are decorative game simulations, not interactive games or telemetry. mini_cells uses local Random.new with seed 73 by default; callers may keep a different integer seed per session. Six immutable replays are cached. Tetrominoes stack and clear; snake pursues food and grows; Pong rallies include imperfect paddle tracking, misses and score pips between serves. Blocks use horizontal half-cells with independent foreground/background colors, filling the pane below PWN (up to 64 cells). Other games retain quadrant pixels; snake stays centered with food/head color priority. White line-clear flashes and game-over/reset wipes are intentional. Pong uses half-cell motion and one-cell-high paddles without speeding up play. Asteroids uses five-pixel cyan arrowheads on a half-cell grid, sparse small white rocks, yellow shots and red thrust; wrapped collisions split real rocks. Static Banner modules autoload only when needed. For more information, see: http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
16
16
|
|
|
17
17
|
## When to use
|
|
18
18
|
|
data/lib/pwn/banner.rb
CHANGED
|
@@ -4,7 +4,8 @@ module PWN
|
|
|
4
4
|
# Static banners and pure, PWN-branded retro ASCII/color loops: falling_blocks,
|
|
5
5
|
# snake, pong, and asteroids. mini_frame returns fresh rows on a borderless canvas;
|
|
6
6
|
# use equal width and height (5..16) for complete square artwork. Dimensions
|
|
7
|
-
# clamp to 0..16 (colored blocks: 0..64)
|
|
7
|
+
# clamp to 0..16 (colored blocks: 0..64). Set branding: false for an unbranded
|
|
8
|
+
# pane with the wordmark row reclaimed for play; tiny panes then stay blank. The caller
|
|
8
9
|
# samples mini_names once per session and advances the explicit frame index
|
|
9
10
|
# every mini_frame_seconds(name:) (0.05 for Pong/Asteroids, 0.1 otherwise).
|
|
10
11
|
# mini_cells replays 1800 seeded gameplay
|
|
@@ -98,7 +99,8 @@ module PWN
|
|
|
98
99
|
# name: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',
|
|
99
100
|
# frame: 'optional - integer frame index, defaults to zero and wraps at 60',
|
|
100
101
|
# width: 'optional - output columns clamped to zero through 16, defaults to 16',
|
|
101
|
-
# height: 'optional - output rows clamped to zero through 16, defaults to 16'
|
|
102
|
+
# height: 'optional - output rows clamped to zero through 16, defaults to 16',
|
|
103
|
+
# branding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'
|
|
102
104
|
# )
|
|
103
105
|
|
|
104
106
|
public_class_method def self.mini_frame(opts = {})
|
|
@@ -110,12 +112,16 @@ module PWN
|
|
|
110
112
|
height = (opts[:height] || MINI_HEIGHT).to_i.clamp(0, MINI_HEIGHT)
|
|
111
113
|
return [] if width.zero? || height.zero?
|
|
112
114
|
|
|
115
|
+
branding = opts[:branding] != false
|
|
116
|
+
return Array.new(height) { ' ' * width } if !branding && (width < 5 || height < 5)
|
|
117
|
+
|
|
118
|
+
height += 1 unless branding
|
|
113
119
|
rows = Array.new(height) { ' ' * width }
|
|
114
120
|
rows[0] = 'PWN'[0, width].center(width)
|
|
115
121
|
return rows if width < 5 || height < 5
|
|
116
122
|
|
|
117
123
|
send("mini_#{name}", rows: rows, phase: phase, width: width, height: height)
|
|
118
|
-
rows
|
|
124
|
+
branding ? rows : rows.drop(1)
|
|
119
125
|
end
|
|
120
126
|
|
|
121
127
|
# Supported Method Parameters::
|
|
@@ -150,7 +156,8 @@ module PWN
|
|
|
150
156
|
# frame: 'optional - integer index, defaults to zero and wraps at 1800',
|
|
151
157
|
# width: 'optional - columns clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',
|
|
152
158
|
# height: 'optional - rows clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',
|
|
153
|
-
# seed: 'optional - integer local PRNG seed, defaults to 73; retain per session'
|
|
159
|
+
# seed: 'optional - integer local PRNG seed, defaults to 73; retain per session',
|
|
160
|
+
# branding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'
|
|
154
161
|
# )
|
|
155
162
|
|
|
156
163
|
public_class_method def self.mini_cells(opts = {})
|
|
@@ -163,10 +170,14 @@ module PWN
|
|
|
163
170
|
height = (opts[:height] || MINI_HEIGHT).to_i.clamp(0, limit)
|
|
164
171
|
return [] if width.zero? || height.zero?
|
|
165
172
|
|
|
173
|
+
branding = opts[:branding] != false
|
|
174
|
+
# Simulate one extra row before removing the legacy label, so every output
|
|
175
|
+
# row belongs to gameplay without changing the public dimension limits.
|
|
166
176
|
rows = if width < 5 || height < 5
|
|
167
|
-
['PWN'[0, width].center(width)] + Array.new(height - 1) { ' ' * width }
|
|
177
|
+
[branding ? 'PWN'[0, width].center(width) : ' ' * width] + Array.new(height - 1) { ' ' * width }
|
|
168
178
|
else
|
|
169
|
-
mini_replay(name: name, width: width, height: height, seed: (opts[:seed] || MINI_SEED).to_i)
|
|
179
|
+
replay = mini_replay(name: name, width: width, height: height + (branding ? 0 : 1), seed: (opts[:seed] || MINI_SEED).to_i)
|
|
180
|
+
branding ? replay[phase][:rows] : replay[phase][:rows].drop(1)
|
|
170
181
|
end
|
|
171
182
|
rows.map do |row|
|
|
172
183
|
row.chars.map do |pixel|
|
|
@@ -823,7 +834,8 @@ module PWN
|
|
|
823
834
|
name: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',
|
|
824
835
|
frame: 'optional - integer frame index, defaults to zero and wraps at 60',
|
|
825
836
|
width: 'optional - columns clamped to zero through 16; defaults to 16',
|
|
826
|
-
height: 'optional - rows clamped to zero through 16; defaults to 16'
|
|
837
|
+
height: 'optional - rows clamped to zero through 16; defaults to 16',
|
|
838
|
+
branding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'
|
|
827
839
|
)
|
|
828
840
|
|
|
829
841
|
# Return fresh Unicode block cells: { glyph:, foreground:, background: }.
|
|
@@ -838,7 +850,8 @@ module PWN
|
|
|
838
850
|
frame: 'optional - integer frame index, defaults to zero and wraps at 1800',
|
|
839
851
|
width: 'optional - columns clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',
|
|
840
852
|
height: 'optional - rows clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',
|
|
841
|
-
seed: 'optional - integer local PRNG seed, defaults to 73; retain per session'
|
|
853
|
+
seed: 'optional - integer local PRNG seed, defaults to 73; retain per session',
|
|
854
|
+
branding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'
|
|
842
855
|
)
|
|
843
856
|
|
|
844
857
|
# Run welcome and return its result
|
|
@@ -1042,7 +1042,7 @@ module PWN
|
|
|
1042
1042
|
put(top + row, left + box_width - 1, '│', color)
|
|
1043
1043
|
end
|
|
1044
1044
|
put(top + height - 1, left, "╰#{'─' * (box_width - 2)}╯", color)
|
|
1045
|
-
put(top, left + 2, fit(" #{title} ", box_width - 4), tone(:title))
|
|
1045
|
+
put(top, left + 2, fit(" #{title} ", box_width - 4), tone(:title)) unless title.empty?
|
|
1046
1046
|
end
|
|
1047
1047
|
|
|
1048
1048
|
def seed_request_history
|
|
@@ -1138,9 +1138,9 @@ module PWN
|
|
|
1138
1138
|
end
|
|
1139
1139
|
cadence = cells ? @banner_frame_seconds : PWN::Banner::MINI_FRAME_SECONDS
|
|
1140
1140
|
frame = ((now - @banner_started) / cadence).floor % PWN::Banner::MINI_FRAME_COUNT
|
|
1141
|
-
return PWN::Banner.mini_cells(name: @banner_name, frame: frame, width: size, height: size, seed: @banner_seed) if cells
|
|
1141
|
+
return PWN::Banner.mini_cells(name: @banner_name, frame: frame, width: size, height: size, seed: @banner_seed, branding: false) if cells
|
|
1142
1142
|
|
|
1143
|
-
art = PWN::Banner.mini_frame(name: @banner_name, frame: frame, width: size, height: size)
|
|
1143
|
+
art = PWN::Banner.mini_frame(name: @banner_name, frame: frame, width: size, height: size, branding: false)
|
|
1144
1144
|
canvas = Array.new(size) { ' ' * size }
|
|
1145
1145
|
top = (size - art.length) / 2
|
|
1146
1146
|
art.each_with_index { |row, index| canvas[top + index] = row.center(size) }
|
|
@@ -1208,7 +1208,7 @@ module PWN
|
|
|
1208
1208
|
put(1, @header_text_column, fit("#{state} · #{elapsed} · #{scroll_status}", @header_text_width), tone(:status))
|
|
1209
1209
|
visible.each_with_index { |spans, index| put_spans(2 + index, @header_text_column, spans) }
|
|
1210
1210
|
if @header_pane_height
|
|
1211
|
-
box(0, 0, height, height, '
|
|
1211
|
+
box(0, 0, height, height, '')
|
|
1212
1212
|
draw_banner(height - 2)
|
|
1213
1213
|
end
|
|
1214
1214
|
height
|
data/lib/pwn/version.rb
CHANGED
data/spec/lib/pwn/banner_spec.rb
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe PWN::Banner do
|
|
6
|
+
it 'reclaims the wordmark row for unbranded games and leaves tiny panes blank' do
|
|
7
|
+
described_class.mini_names.product([1, 3, 5, 8, 16]).each do |name, size|
|
|
8
|
+
[0, 6, 100, 1799].each do |frame|
|
|
9
|
+
args = { name: name, width: size, height: size, frame: frame, branding: false }
|
|
10
|
+
cells = described_class.mini_cells(**args)
|
|
11
|
+
ascii = described_class.mini_frame(**args)
|
|
12
|
+
expect(cells.length).to eq(size)
|
|
13
|
+
expect(cells.map(&:length)).to all(eq(size))
|
|
14
|
+
expect(cells.flatten.map { |cell| cell[:glyph] }.join).not_to match(/[PWN]/)
|
|
15
|
+
expect(ascii.length).to eq(size)
|
|
16
|
+
expect(ascii.map(&:length)).to all(eq(size))
|
|
17
|
+
expect(ascii.join).not_to match(/[PWN]/)
|
|
18
|
+
next if size < 5
|
|
19
|
+
|
|
20
|
+
replay = described_class.send(:mini_replay, name: name, width: size, height: size + 1, seed: 73)
|
|
21
|
+
expected = replay[frame][:rows].drop(1).map do |row|
|
|
22
|
+
row.chars.map { |pixel| described_class::MINI_PIXELS.fetch(pixel).first }.join
|
|
23
|
+
end
|
|
24
|
+
expect(cells.map { |row| row.map { |cell| cell[:glyph] }.join }).to eq(expected)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
6
29
|
def block_colors(rows)
|
|
7
30
|
rows.flat_map do |row|
|
|
8
31
|
[0, 2].map do |bit|
|
|
@@ -31,9 +54,9 @@ describe PWN::Banner do
|
|
|
31
54
|
|
|
32
55
|
it 'keeps every active tetromino color beside the stack throughout its fall and lock' do
|
|
33
56
|
rotations = Hash.new { |hash, key| hash[key] = [] }
|
|
34
|
-
[5, 8, 16, 20].product([1, 73, 74]).each do |size, seed|
|
|
35
|
-
args = { name: :falling_blocks, width: size, height: size, seed: seed }
|
|
36
|
-
frames = described_class.send(:mini_replay, **args)
|
|
57
|
+
[5, 8, 16, 20].product([1, 73, 74], [true, false]).each do |size, seed, branding|
|
|
58
|
+
args = { name: :falling_blocks, width: size, height: size, seed: seed, branding: branding }
|
|
59
|
+
frames = described_class.send(:mini_replay, **args, height: size + (branding ? 0 : 1))
|
|
37
60
|
frames.each_with_index do |frame, index|
|
|
38
61
|
next unless frame[:state][:active]
|
|
39
62
|
|
|
@@ -45,13 +68,17 @@ describe PWN::Banner do
|
|
|
45
68
|
points.each { |x, y| expected[y][x] = color }
|
|
46
69
|
expect(block_colors(frame[:rows].drop(1))).to eq(expected)
|
|
47
70
|
points.each do |x, y|
|
|
48
|
-
|
|
71
|
+
expect(x).to be_between(0, size - 1)
|
|
72
|
+
expect(y).to be_between(0, ((size - (branding ? 1 : 0)) * 2) - 1)
|
|
73
|
+
cell = cells[(branding ? 1 : 0) + (y / 2)][x]
|
|
49
74
|
mask = ' ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█'.index(cell[:glyph])
|
|
50
75
|
actual = mask[(y % 2) * 2] == 1 ? cell[:foreground] : cell[:background]
|
|
51
76
|
expect(actual).to eq(color)
|
|
52
77
|
end
|
|
53
78
|
next unless frames[index + 1][:event] == :lock
|
|
54
79
|
|
|
80
|
+
stack = frame[:state][:stack]
|
|
81
|
+
expect(points.any? { |x, y| y == stack.length - 1 || stack[y + 1][x] != ' ' }).to be(true)
|
|
55
82
|
expect(frames[index + 1][:rows]).to eq(frame[:rows])
|
|
56
83
|
expect(described_class.mini_cells(**args, frame: index + 1)).to eq(cells)
|
|
57
84
|
rotations[type] << points.map { |x, y| [x - points.map(&:first).min, y - points.map(&:last).min] }.sort
|
|
@@ -425,6 +425,25 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
425
425
|
PWN::Env[:ai] = previous
|
|
426
426
|
end
|
|
427
427
|
|
|
428
|
+
it 'draws every game without wordmark glyphs in color, monochrome and tiny panes' do
|
|
429
|
+
PWN::Banner.mini_names.product([1, 3, 5, 8, 16, 20], [true, false]).each do |name, size, colors|
|
|
430
|
+
console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
|
|
431
|
+
allow(PWN::Banner).to receive(:mini_names).and_return([name])
|
|
432
|
+
now = 100.0
|
|
433
|
+
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC) { now }
|
|
434
|
+
console.instance_variable_set(:@banner_colors, colors)
|
|
435
|
+
console.instance_variable_set(:@banner_two_colors, colors)
|
|
436
|
+
calls = []
|
|
437
|
+
allow(console).to receive(:put) { |*args| calls << args }
|
|
438
|
+
console.draw_banner(size)
|
|
439
|
+
now += 10.0
|
|
440
|
+
console.draw_banner(size)
|
|
441
|
+
expect(calls.map { |_, _, text| text }.join).not_to match(/[PWN]/)
|
|
442
|
+
expect(calls).to all(satisfy { |y, x, text| y.between?(1, size) && x >= 1 && x + text.length <= size + 1 })
|
|
443
|
+
expect(console.banner_frame(size).join).not_to match(/[PWN]/)
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
428
447
|
it 'selects one mini-banner per session and advances only from monotonic elapsed time' do
|
|
429
448
|
pry = Pry.new
|
|
430
449
|
pry.config.pwn_ai_session_id = 'banner-one'
|
|
@@ -435,16 +454,16 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
435
454
|
now = 100.0
|
|
436
455
|
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC) { now }
|
|
437
456
|
first = console.banner_frame(8)
|
|
438
|
-
expect(first).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 0, width: 8, height: 8))
|
|
457
|
+
expect(first).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 0, width: 8, height: 8, branding: false))
|
|
439
458
|
now += PWN::Banner::MINI_FRAME_SECONDS / 2
|
|
440
459
|
expect(console.banner_frame(8)).to eq(first)
|
|
441
460
|
now += PWN::Banner::MINI_FRAME_SECONDS * 0.75
|
|
442
|
-
expect(console.banner_frame(8)).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 1, width: 8, height: 8))
|
|
461
|
+
expect(console.banner_frame(8)).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 1, width: 8, height: 8, branding: false))
|
|
443
462
|
now += PWN::Banner::MINI_FRAME_SECONDS * PWN::Banner::MINI_FRAME_COUNT
|
|
444
|
-
expect(console.banner_frame(12)).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 1, width: 12, height: 12))
|
|
463
|
+
expect(console.banner_frame(12)).to eq(PWN::Banner.mini_frame(name: choices.first, frame: 1, width: 12, height: 12, branding: false))
|
|
445
464
|
expect(choices).to have_received(:sample).once
|
|
446
465
|
pry.config.pwn_ai_session_id = 'banner-two'
|
|
447
|
-
expect(console.banner_frame(8)).to eq(PWN::Banner.mini_frame(name: choices.last, frame: 0, width: 8, height: 8))
|
|
466
|
+
expect(console.banner_frame(8)).to eq(PWN::Banner.mini_frame(name: choices.last, frame: 0, width: 8, height: 8, branding: false))
|
|
448
467
|
expect(choices).to have_received(:sample).twice
|
|
449
468
|
end
|
|
450
469
|
|
|
@@ -454,7 +473,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
454
473
|
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0)
|
|
455
474
|
size = 24
|
|
456
475
|
canvas = console.banner_frame(size)
|
|
457
|
-
art = PWN::Banner.mini_frame(name: :pong, frame: 0, width: size, height: size)
|
|
476
|
+
art = PWN::Banner.mini_frame(name: :pong, frame: 0, width: size, height: size, branding: false)
|
|
458
477
|
expect(canvas.length).to eq(size)
|
|
459
478
|
expect(canvas.map(&:length)).to all(eq(size))
|
|
460
479
|
top = (size - art.length) / 2
|
|
@@ -470,14 +489,14 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
470
489
|
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC) { now }
|
|
471
490
|
console.banner_frame(20, cells: true)
|
|
472
491
|
seed = console.instance_variable_get(:@banner_seed)
|
|
473
|
-
frames = PWN::Banner.send(:mini_replay, name: :falling_blocks, width: 20, height:
|
|
492
|
+
frames = PWN::Banner.send(:mini_replay, name: :falling_blocks, width: 20, height: 21, seed: seed)
|
|
474
493
|
tick = frames.index { |frame| frame[:event] == :lock }
|
|
475
494
|
now += (tick + 0.1) * PWN::Banner::MINI_FRAME_SECONDS
|
|
476
495
|
calls = []
|
|
477
496
|
allow(console).to receive(:put) { |*args| calls << args }
|
|
478
497
|
console.draw_banner(20)
|
|
479
498
|
expect(calls.select { |y, _x, text| y == 20 && text.strip != '' }).not_to be_empty
|
|
480
|
-
expect(calls
|
|
499
|
+
expect(calls.map { |_, _, text| text }.join).not_to match(/[PWN]/)
|
|
481
500
|
expect(calls).to all(satisfy { |y, x, text| y.between?(1, 20) && x >= 1 && x + text.length <= 21 })
|
|
482
501
|
end
|
|
483
502
|
|
|
@@ -498,12 +517,13 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
498
517
|
end
|
|
499
518
|
allow(PWN::Banner).to receive(:mini_cells).and_call_original
|
|
500
519
|
console.draw
|
|
501
|
-
expect(rectangles).to include([0, 0, 10, 10, '
|
|
520
|
+
expect(rectangles).to include([0, 0, 10, 10, ''])
|
|
502
521
|
expect(rectangles).to include([0, 10, 10, 109, "pwn-ai v#{PWN::VERSION}"])
|
|
503
522
|
expect(console.instance_variable_get(:@header_text_column)).to eq(12)
|
|
504
523
|
expect(painted).to include([2, 12, 'PROVIDER (ENGINE):', console.tone(:category)])
|
|
505
524
|
expect(painted.any? { |y, x, text, color| y == 0 && x == 0 && text.include?('╭') && color == console.tone(:border) }).to be(true)
|
|
506
|
-
expect(painted.any? { |y, x,
|
|
525
|
+
expect(painted.any? { |y, x, _text, _color| y == 0 && x == 2 }).to be(false)
|
|
526
|
+
expect(painted.select { |y, x, _text, _color| y < 10 && x < 10 }.map { |_, _, text| text }.join).not_to match(/[PWN]/)
|
|
507
527
|
expect(painted.select { |y, x, text, _color| y.between?(1, 8) && x == 1 && text == ' ' * 8 }.length).to eq(8)
|
|
508
528
|
expect(painted.select { |y, _x, _text, _color| y < 10 }).to all(satisfy { |y, x, text, _color| y >= 0 && x >= 0 && x + console.width(text) <= 120 })
|
|
509
529
|
expect(PWN::Banner).to have_received(:mini_cells).with(hash_including(width: 8, height: 8)).once
|
|
@@ -511,7 +531,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
511
531
|
allow(curses).to receive_messages(cols: columns, lines: rows)
|
|
512
532
|
rectangles.clear
|
|
513
533
|
console.draw
|
|
514
|
-
expect(rectangles.map(&:last)).not_to include('
|
|
534
|
+
expect(rectangles.map(&:last)).not_to include('')
|
|
515
535
|
expect(console.instance_variable_get(:@header_text_column)).to eq(2)
|
|
516
536
|
end
|
|
517
537
|
allow(curses).to receive_messages(cols: 120, lines: 26)
|
|
@@ -533,7 +553,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
533
553
|
allow(PWN::Banner).to receive(:mini_cells).and_call_original
|
|
534
554
|
height = console.draw_header('ollama', 'fixture')
|
|
535
555
|
expect(height).to be > 10
|
|
536
|
-
expect(console).to have_received(:box).with(0, 0, height, height, '
|
|
556
|
+
expect(console).to have_received(:box).with(0, 0, height, height, '')
|
|
537
557
|
expect(PWN::Banner).to have_received(:mini_cells).with(hash_including(width: height - 2, height: height - 2))
|
|
538
558
|
expect(console.instance_variable_get(:@header_spans).length + 3).to be <= height
|
|
539
559
|
expect(console.header_lines('ollama', 'fixture', console.instance_variable_get(:@header_text_width)).join).to include('REASONING EFFORT: high')
|
|
@@ -605,10 +625,10 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
605
625
|
console.banner_frame(8, cells: true)
|
|
606
626
|
seed = console.instance_variable_get(:@banner_seed)
|
|
607
627
|
expect(seed).to be_a(Integer)
|
|
608
|
-
expect(console.banner_frame(8, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 0, width: 8, height: 8, seed: seed))
|
|
628
|
+
expect(console.banner_frame(8, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 0, width: 8, height: 8, seed: seed, branding: false))
|
|
609
629
|
now += 0.21
|
|
610
|
-
expect(console.banner_frame(8, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 2, width: 8, height: 8, seed: seed))
|
|
611
|
-
expect(console.banner_frame(12, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 2, width: 12, height: 12, seed: seed))
|
|
630
|
+
expect(console.banner_frame(8, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 2, width: 8, height: 8, seed: seed, branding: false))
|
|
631
|
+
expect(console.banner_frame(12, cells: true)).to eq(PWN::Banner.mini_cells(name: :snake, frame: 2, width: 12, height: 12, seed: seed, branding: false))
|
|
612
632
|
expect(console.instance_variable_get(:@banner_seed)).to eq(seed)
|
|
613
633
|
pry.config.pwn_ai_session_id = 'another-colored-session'
|
|
614
634
|
console.banner_frame(8, cells: true)
|
|
@@ -630,7 +650,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
|
|
|
630
650
|
console.banner_frame(8, cells: true)
|
|
631
651
|
seed = console.instance_variable_get(:@banner_seed)
|
|
632
652
|
now += 1.21
|
|
633
|
-
expect(PWN::Banner).to receive(:mini_cells).with(name: name, frame: 24, width: 8, height: 8, seed: seed).twice.and_call_original
|
|
653
|
+
expect(PWN::Banner).to receive(:mini_cells).with(name: name, frame: 24, width: 8, height: 8, seed: seed, branding: false).twice.and_call_original
|
|
634
654
|
2.times { console.banner_frame(8, cells: true) }
|
|
635
655
|
end
|
|
636
656
|
end
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -1708,12 +1708,13 @@
|
|
|
1708
1708
|
{"messages":[{"role":"user","content":"PWN::Banner.help Usage"},{"role":"assistant","content":"`PWN::Banner.help`: "}]}
|
|
1709
1709
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_asteroids Usage"},{"role":"assistant","content":"`PWN::Banner.mini_asteroids`: "}]}
|
|
1710
1710
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_asteroids_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_asteroids_snapshot`: "}]}
|
|
1711
|
-
{"messages":[{"role":"user","content":"PWN::Banner.
|
|
1711
|
+
{"messages":[{"role":"user","content":"PWN::Banner.mini_block_board Usage"},{"role":"assistant","content":"`PWN::Banner.mini_block_board`: "}]}
|
|
1712
|
+
{"messages":[{"role":"user","content":"PWN::Banner.mini_cells Usage"},{"role":"assistant","content":"`PWN::Banner.mini_cells`: Supported Method Parameters\n\nReturn rows of single-column Unicode block cells, with named terminal colors. Each fresh cell has glyph, foreground and background keys; no ANSI or IO. Square means terminal-cell dimensions, not physical pixels. Caller owns borders. Use mini_frame_seconds for cadence; 1800 frames span 90 or 180 seconds. Colored blocks fill the canvas with half-cell pieces; other games cap at 16. PWN::Banner.mini_cells(\n\nname: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',\nframe: 'optional - integer index, defaults to zero and wraps at 1800',\nwidth: 'optional - columns clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',\nheight: 'optional - rows clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',\nseed: 'optional - integer local PRNG seed, defaults to 73; retain per session',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
|
|
1712
1713
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_delta Usage"},{"role":"assistant","content":"`PWN::Banner.mini_delta`: "}]}
|
|
1713
1714
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_emit Usage"},{"role":"assistant","content":"`PWN::Banner.mini_emit`: "}]}
|
|
1714
1715
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_falling_blocks Usage"},{"role":"assistant","content":"`PWN::Banner.mini_falling_blocks`: "}]}
|
|
1715
1716
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_fits? Usage"},{"role":"assistant","content":"`PWN::Banner.mini_fits?`: "}]}
|
|
1716
|
-
{"messages":[{"role":"user","content":"PWN::Banner.mini_frame Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame`: Supported Method Parameters\n\nReturn fresh ASCII rows for decorative retro loops, not interactive games. Use equal width and height for a square; 5..16 retains the complete artwork. Smaller panes degrade to a wordmark. Caller owns randomness, clock and IO. PWN::Banner.mini_frame(\n\nname: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',\nframe: 'optional - integer frame index, defaults to zero and wraps at 60',\nwidth: 'optional - output columns clamped to zero through 16, defaults to 16',\nheight: 'optional - output rows clamped to zero through 16, defaults to 16'\n\n)\n"}]}
|
|
1717
|
+
{"messages":[{"role":"user","content":"PWN::Banner.mini_frame Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame`: Supported Method Parameters\n\nReturn fresh ASCII rows for decorative retro loops, not interactive games. Use equal width and height for a square; 5..16 retains the complete artwork. Smaller panes degrade to a wordmark. Caller owns randomness, clock and IO. PWN::Banner.mini_frame(\n\nname: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',\nframe: 'optional - integer frame index, defaults to zero and wraps at 60',\nwidth: 'optional - output columns clamped to zero through 16, defaults to 16',\nheight: 'optional - output rows clamped to zero through 16, defaults to 16',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
|
|
1717
1718
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_frame_seconds Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame_seconds`: Supported Method Parameters\n\nReturn the presentation interval; simulations preserve their gameplay speed. PWN::Banner.mini_frame_seconds(\n\nname: 'optional - animation name, defaults to falling_blocks'\n\n)\n"}]}
|
|
1718
1719
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_freeze Usage"},{"role":"assistant","content":"`PWN::Banner.mini_freeze`: "}]}
|
|
1719
1720
|
{"messages":[{"role":"user","content":"PWN::Banner.mini_landing Usage"},{"role":"assistant","content":"`PWN::Banner.mini_landing`: "}]}
|