a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
1package coninput
2
3import (
4 "encoding/binary"
5 "fmt"
6 "strconv"
7 "strings"
8)
9
10const (
11 maxEventSize = 16
12 wordPaddingBytes = 2
13)
14
15// EventType denots the type of an event
16type EventType uint16
17
18// EventUnion is the union data type that contains the data for any event.
19type EventUnion [maxEventSize]byte
20
21// InputRecord corresponds to the INPUT_RECORD structure from the Windows
22// console API (see
23// https://docs.microsoft.com/en-us/windows/console/input-record-str).
24type InputRecord struct {
25 // EventType specifies the type of event that helt in Event.
26 EventType EventType
27
28 // Padding of the 16-bit EventType to a whole 32-bit dword.
29 _ [wordPaddingBytes]byte
30
31 // Event holds the actual event data. Use Unrap to access it as its
32 // respective event type.
33 Event EventUnion
34}
35
36// String implements fmt.Stringer for InputRecord.
37func (ir InputRecord) String() string {
38 return ir.Unwrap().String()
39}
40
41// Unwrap parses the event data into an EventRecord of the respective event
42// type. The data in the returned EventRecord does not contain any references to
43// the passed InputRecord.
44func (ir InputRecord) Unwrap() EventRecord {
45 switch ir.EventType {
46 case FocusEventType:
47 return FocusEventRecord{SetFocus: ir.Event[0] > 0}
48 case KeyEventType:
49 return KeyEventRecord{
50 KeyDown: binary.LittleEndian.Uint32(ir.Event[0:4]) > 0,
51 RepeatCount: binary.LittleEndian.Uint16(ir.Event[4:6]),
52 VirtualKeyCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[6:8])),
53 VirtualScanCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[8:10])),
54 Char: rune(binary.LittleEndian.Uint16(ir.Event[10:12])),
55 ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[12:16])),
56 }
57 case MouseEventType:
58 m := MouseEventRecord{
59 MousePositon: Coord{
60 X: binary.LittleEndian.Uint16(ir.Event[0:2]),
61 Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
62 },
63 ButtonState: ButtonState(binary.LittleEndian.Uint32(ir.Event[4:8])),
64 ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[8:12])),
65 EventFlags: EventFlags(binary.LittleEndian.Uint32(ir.Event[12:16])),
66 }
67
68 if (m.EventFlags&MOUSE_WHEELED > 0) || (m.EventFlags&MOUSE_HWHEELED > 0) {
69 if int16(highWord(uint32(m.ButtonState))) > 0 {
70 m.WheelDirection = 1
71 } else {
72 m.WheelDirection = -1
73 }
74 }
75
76 return m
77 case WindowBufferSizeEventType:
78 return WindowBufferSizeEventRecord{
79 Size: Coord{
80 X: binary.LittleEndian.Uint16(ir.Event[0:2]),
81 Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
82 },
83 }
84 case MenuEventType:
85 return MenuEventRecord{
86 CommandID: binary.LittleEndian.Uint32(ir.Event[0:4]),
87 }
88 default:
89 return &UnknownEvent{InputRecord: ir}
90 }
91}
92
93// EventRecord represents one of the following event types:
94// TypeFocusEventRecord, TypeKeyEventRecord, TypeMouseEventRecord,
95// TypeWindowBufferSizeEvent, TypeMenuEventRecord and UnknownEvent.
96type EventRecord interface {
97 Type() string
98 fmt.Stringer
99}
100
101// FocusEventType is the event type for a FocusEventRecord (see
102// https://docs.microsoft.com/en-us/windows/console/input-record-str).
103const FocusEventType EventType = 0x0010
104
105// FocusEventRecord represent the FOCUS_EVENT_RECORD structure from the Windows
106// console API (see
107// https://docs.microsoft.com/en-us/windows/console/focus-event-record-str).
108// These events are used internally by the Windows console API and should be
109// ignored.
110type FocusEventRecord struct {
111 // SetFocus is reserved and should not be used.
112 SetFocus bool
113}
114
115// Ensure that FocusEventRecord satisfies EventRecord interface.
116var _ EventRecord = FocusEventRecord{}
117
118// Type ensures that FocusEventRecord satisfies EventRecord interface.
119func (e FocusEventRecord) Type() string { return "FocusEvent" }
120
121// String ensures that FocusEventRecord satisfies EventRecord and fmt.Stringer
122// interfaces.
123func (e FocusEventRecord) String() string { return fmt.Sprintf("%s[%v]", e.Type(), e.SetFocus) }
124
125// KeyEventType is the event type for a KeyEventRecord (see
126// https://docs.microsoft.com/en-us/windows/console/input-record-str).
127const KeyEventType EventType = 0x0001
128
129// KeyEventRecord represent the KEY_EVENT_RECORD structure from the Windows
130// console API (see
131// https://docs.microsoft.com/en-us/windows/console/key-event-record-str).
132type KeyEventRecord struct {
133 // KeyDown specified whether the key is pressed or released.
134 KeyDown bool
135
136 // RepeatCount indicates that a key is being held down. For example, when a
137 // key is held down, five events with RepeatCount equal to 1 may be
138 // generated, one event with RepeatCount equal to 5, or multiple events
139 // with RepeatCount greater than or equal to 1.
140 RepeatCount uint16
141
142 // VirtualKeyCode identifies the given key in a device-independent manner
143 // (see
144 // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
145 VirtualKeyCode VirtualKeyCode
146
147 // VirtualScanCode represents the device-dependent value generated by the
148 // keyboard hardware.
149 VirtualScanCode VirtualKeyCode
150
151 // Char is the character that corresponds to the pressed key. Char can be
152 // zero for some keys.
153 Char rune
154
155 //ControlKeyState holds the state of the control keys.
156 ControlKeyState ControlKeyState
157}
158
159// Ensure that KeyEventRecord satisfies EventRecord interface.
160var _ EventRecord = KeyEventRecord{}
161
162// Type ensures that KeyEventRecord satisfies EventRecord interface.
163func (e KeyEventRecord) Type() string { return "KeyEvent" }
164
165// String ensures that KeyEventRecord satisfies EventRecord and fmt.Stringer
166// interfaces.
167func (e KeyEventRecord) String() string {
168 infos := []string{}
169
170 repeat := ""
171 if e.RepeatCount > 1 {
172 repeat = "x" + strconv.Itoa(int(e.RepeatCount))
173 }
174
175 infos = append(infos, fmt.Sprintf("%q%s", e.Char, repeat))
176
177 direction := "up"
178 if e.KeyDown {
179 direction = "down"
180 }
181
182 infos = append(infos, direction)
183
184 if e.ControlKeyState != NO_CONTROL_KEY {
185 infos = append(infos, e.ControlKeyState.String())
186 }
187
188 infos = append(infos, fmt.Sprintf("KeyCode: %d", e.VirtualKeyCode))
189 infos = append(infos, fmt.Sprintf("ScanCode: %d", e.VirtualScanCode))
190
191 return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
192}
193
194// MenuEventType is the event type for a MenuEventRecord (see
195// https://docs.microsoft.com/en-us/windows/console/input-record-str).
196const MenuEventType EventType = 0x0008
197
198// MenuEventRecord represent the MENU_EVENT_RECORD structure from the Windows
199// console API (see
200// https://docs.microsoft.com/en-us/windows/console/menu-event-record-str).
201// These events are deprecated by the Windows console API and should be ignored.
202type MenuEventRecord struct {
203 CommandID uint32
204}
205
206// Ensure that MenuEventRecord satisfies EventRecord interface.
207var _ EventRecord = MenuEventRecord{}
208
209// Type ensures that MenuEventRecord satisfies EventRecord interface.
210func (e MenuEventRecord) Type() string { return "MenuEvent" }
211
212// String ensures that MenuEventRecord satisfies EventRecord and fmt.Stringer
213// interfaces.
214func (e MenuEventRecord) String() string { return fmt.Sprintf("MenuEvent[%d]", e.CommandID) }
215
216// MouseEventType is the event type for a MouseEventRecord (see
217// https://docs.microsoft.com/en-us/windows/console/input-record-str).
218const MouseEventType EventType = 0x0002
219
220// MouseEventRecord represent the MOUSE_EVENT_RECORD structure from the Windows
221// console API (see
222// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
223type MouseEventRecord struct {
224 // MousePosition contains the location of the cursor, in terms of the
225 // console screen buffer's character-cell coordinates.
226 MousePositon Coord
227
228 // ButtonState holds the status of the mouse buttons.
229 ButtonState ButtonState
230
231 // ControlKeyState holds the state of the control keys.
232 ControlKeyState ControlKeyState
233
234 // EventFlags specify tge type of mouse event.
235 EventFlags EventFlags
236
237 // WheelDirection specified the direction in which the mouse wheel is
238 // spinning when EventFlags contains MOUSE_HWHEELED or MOUSE_WHEELED. When
239 // the event flags specify MOUSE_WHEELED it is 1 if the wheel rotated
240 // forward (away from the user) or -1 when it rotates backwards. When
241 // MOUSE_HWHEELED is specified it is 1 when the wheel rotates right and -1
242 // when it rotates left. When the EventFlags do not indicate a mouse wheel
243 // event it is 0.
244 WheelDirection int
245}
246
247// Ensure that MouseEventRecord satisfies EventRecord interface.
248var _ EventRecord = MouseEventRecord{}
249
250func (e MouseEventRecord) WheelDirectionName() string {
251 if e.EventFlags&MOUSE_WHEELED > 0 {
252 if e.WheelDirection > 0 {
253 return "Forward"
254 }
255
256 return "Backward"
257 } else if e.EventFlags&MOUSE_HWHEELED > 0 {
258 if e.WheelDirection > 0 {
259 return "Right"
260 }
261
262 return "Left"
263 }
264
265 return ""
266}
267
268// Type ensures that MouseEventRecord satisfies EventRecord interface.
269func (e MouseEventRecord) Type() string { return "MouseEvent" }
270
271// String ensures that MouseEventRecord satisfies EventRecord and fmt.Stringer
272// interfaces.
273func (e MouseEventRecord) String() string {
274 infos := []string{e.MousePositon.String()}
275
276 if e.ButtonState&0xFF != 0 {
277 infos = append(infos, e.ButtonState.String())
278 }
279
280 eventDescription := e.EventFlags.String()
281
282 wheelDirection := e.WheelDirectionName()
283 if wheelDirection != "" {
284 eventDescription += "(" + wheelDirection + ")"
285 }
286
287 infos = append(infos, eventDescription)
288
289 if e.ControlKeyState != NO_CONTROL_KEY {
290 infos = append(infos, e.ControlKeyState.String())
291 }
292
293 return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
294}
295
296// WindowBufferSizeEventType is the event type for a WindowBufferSizeEventRecord
297// (see https://docs.microsoft.com/en-us/windows/console/input-record-str).
298const WindowBufferSizeEventType EventType = 0x0004
299
300// WindowBufferSizeEventRecord represent the WINDOW_BUFFER_SIZE_RECORD structure
301// from the Windows console API (see
302// https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str).
303type WindowBufferSizeEventRecord struct {
304 // Size contains the size of the console screen buffer, in character cell columns and rows.
305 Size Coord
306}
307
308// Ensure that WindowBufferSizeEventRecord satisfies EventRecord interface.
309var _ EventRecord = WindowBufferSizeEventRecord{}
310
311// Type ensures that WindowBufferSizeEventRecord satisfies EventRecord interface.
312func (e WindowBufferSizeEventRecord) Type() string { return "WindowBufferSizeEvent" }
313
314// String ensures that WindowBufferSizeEventRecord satisfies EventRecord and fmt.Stringer
315// interfaces.
316func (e WindowBufferSizeEventRecord) String() string {
317 return fmt.Sprintf("WindowBufferSizeEvent[%s]", e.Size)
318}
319
320// UnknownEvent is generated when the event type does not match one of the
321// following types: TypeFocusEventRecord, TypeKeyEventRecord,
322// TypeMouseEventRecord, TypeWindowBufferSizeEvent, TypeMenuEventRecord and
323// UnknownEvent.
324type UnknownEvent struct {
325 InputRecord
326}
327
328// Ensure that UnknownEvent satisfies EventRecord interface.
329var _ EventRecord = UnknownEvent{}
330
331// Type ensures that UnknownEvent satisfies EventRecord interface.
332func (e UnknownEvent) Type() string { return "UnknownEvent" }
333
334// String ensures that UnknownEvent satisfies EventRecord and fmt.Stringer
335// interfaces.
336func (e UnknownEvent) String() string {
337 return fmt.Sprintf("%s[Type: %d, Data: %v]", e.Type(), e.InputRecord.EventType, e.InputRecord.Event[:])
338}
339
340// Coord represent the COORD structure from the Windows
341// console API (see https://docs.microsoft.com/en-us/windows/console/coord-str).
342type Coord struct {
343 // X is the horizontal coordinate or column value. The units depend on the function call.
344 X uint16
345 // Y is the vertical coordinate or row value. The units depend on the function call.
346 Y uint16
347}
348
349// String ensures that Coord satisfies the fmt.Stringer interface.
350func (c Coord) String() string {
351 return fmt.Sprintf("(%d, %d)", c.X, c.Y)
352}
353
354// ButtonState holds the state of the mouse buttons (see
355// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
356type ButtonState uint32
357
358func (bs ButtonState) Contains(state ButtonState) bool {
359 return bs&state > 0
360}
361
362// String ensures that ButtonState satisfies the fmt.Stringer interface.
363func (bs ButtonState) String() string {
364 switch {
365 case bs&FROM_LEFT_1ST_BUTTON_PRESSED > 0:
366 return "Left"
367 case bs&FROM_LEFT_2ND_BUTTON_PRESSED > 0:
368 return "2"
369 case bs&FROM_LEFT_3RD_BUTTON_PRESSED > 0:
370 return "3"
371 case bs&FROM_LEFT_4TH_BUTTON_PRESSED > 0:
372 return "4"
373 case bs&RIGHTMOST_BUTTON_PRESSED > 0:
374 return "Right"
375 case bs&0xFF == 0:
376 return "No Button"
377 default:
378 return fmt.Sprintf("Unknown(%d)", bs)
379 }
380}
381
382func (bs ButtonState) IsReleased() bool {
383 return bs&0xff > 0
384}
385
386// Valid values for ButtonState.
387const (
388 FROM_LEFT_1ST_BUTTON_PRESSED ButtonState = 0x0001
389 RIGHTMOST_BUTTON_PRESSED ButtonState = 0x0002
390 FROM_LEFT_2ND_BUTTON_PRESSED ButtonState = 0x0004
391 FROM_LEFT_3RD_BUTTON_PRESSED ButtonState = 0x0008
392 FROM_LEFT_4TH_BUTTON_PRESSED ButtonState = 0x0010
393)
394
395// ControlKeyState holds the state of the control keys for key and mouse events
396// (see https://docs.microsoft.com/en-us/windows/console/key-event-record-str
397// and https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
398type ControlKeyState uint32
399
400func (cks ControlKeyState) Contains(state ControlKeyState) bool {
401 return cks&state > 0
402}
403
404// Valid values for ControlKeyState.
405const (
406 CAPSLOCK_ON ControlKeyState = 0x0080
407 ENHANCED_KEY ControlKeyState = 0x0100
408 LEFT_ALT_PRESSED ControlKeyState = 0x0002
409 LEFT_CTRL_PRESSED ControlKeyState = 0x0008
410 NUMLOCK_ON ControlKeyState = 0x0020
411 RIGHT_ALT_PRESSED ControlKeyState = 0x0001
412 RIGHT_CTRL_PRESSED ControlKeyState = 0x0004
413 SCROLLLOCK_ON ControlKeyState = 0x0040
414 SHIFT_PRESSED ControlKeyState = 0x0010
415 NO_CONTROL_KEY ControlKeyState = 0x0000
416)
417
418// String ensures that ControlKeyState satisfies the fmt.Stringer interface.
419func (cks ControlKeyState) String() string {
420 controlKeys := []string{}
421
422 switch {
423 case cks&CAPSLOCK_ON > 0:
424 controlKeys = append(controlKeys, "CapsLock")
425 case cks&ENHANCED_KEY > 0:
426 controlKeys = append(controlKeys, "Enhanced")
427 case cks&LEFT_ALT_PRESSED > 0:
428 controlKeys = append(controlKeys, "Alt")
429 case cks&LEFT_CTRL_PRESSED > 0:
430 controlKeys = append(controlKeys, "CTRL")
431 case cks&NUMLOCK_ON > 0:
432 controlKeys = append(controlKeys, "NumLock")
433 case cks&RIGHT_ALT_PRESSED > 0:
434 controlKeys = append(controlKeys, "RightAlt")
435 case cks&RIGHT_CTRL_PRESSED > 0:
436 controlKeys = append(controlKeys, "RightCTRL")
437 case cks&SCROLLLOCK_ON > 0:
438 controlKeys = append(controlKeys, "ScrollLock")
439 case cks&SHIFT_PRESSED > 0:
440 controlKeys = append(controlKeys, "Shift")
441 case cks == NO_CONTROL_KEY:
442 default:
443 return fmt.Sprintf("Unknown(%d)", cks)
444 }
445
446 return strings.Join(controlKeys, ",")
447}
448
449// EventFlags specifies the type of a mouse event (see
450// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
451type EventFlags uint32
452
453// String ensures that EventFlags satisfies the fmt.Stringer interface.
454func (ef EventFlags) String() string {
455 switch {
456 case ef&DOUBLE_CLICK > 0:
457 return "DoubleClick"
458 case ef&MOUSE_WHEELED > 0:
459 return "Wheeled"
460 case ef&MOUSE_MOVED > 0:
461 return "Moved"
462 case ef&MOUSE_HWHEELED > 0:
463 return "HWheeld"
464 case ef == CLICK:
465 return "Click"
466 default:
467 return fmt.Sprintf("Unknown(%d)", ef)
468 }
469}
470
471func (ef EventFlags) Contains(flag EventFlags) bool {
472 return ef&flag > 0
473}
474
475// Valid values for EventFlags.
476const (
477 CLICK EventFlags = 0x0000
478 MOUSE_MOVED EventFlags = 0x0001
479 DOUBLE_CLICK EventFlags = 0x0002
480 MOUSE_WHEELED EventFlags = 0x0004
481 MOUSE_HWHEELED EventFlags = 0x0008
482)
483
484func highWord(data uint32) uint16 {
485 return uint16((data & 0xFFFF0000) >> 16)
486}