xenos-enigma 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3f9f61779f252a914d06bd33dcb744b4077b0eff04c7fbe59a383d9373e2fd5f
4
+ data.tar.gz: 0b44c9aad8ba9ac1ee81f41f467bb85eb509884e738e86e52fbcad4051840335
5
+ SHA512:
6
+ metadata.gz: 810f480098060ab33000490998d81551d73cd3cd1c1b2df0a6ee7269b8d4d62a568d09aaae6edadeed5620459c05471f95ace4a5b4284e75143500a38f47963f
7
+ data.tar.gz: e63c6eb11e3a310e66a2334bce37be61b4a46ae14a2cfe6dc56d3d10cd43b6ac2aab245c8feec2716086929da984e6a322318c71cd14f674465dd568eeb95ca6
data/CHANGELOG ADDED
@@ -0,0 +1,6 @@
1
+ == 1.0.0 / 13-07-2021
2
+ * ability to detect multiple xenos ships in radar data
3
+ * ability to easily extend xenos supported detection
4
+ * ability to read radar data from file
5
+ * agnostic to dimensions of radar data
6
+ * rspec added
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Haris Krajina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # Xenos Enigma
2
+
3
+ ## Description:
4
+
5
+ In the grim dark future of 40k, there is only war and humanity is under threat again. There are Xenos forces emerging from deep space and the order of Adeptus Mechanicus has been tasked with creating advanced radar systems to detect these threats before it is too late.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gem install xenos-enigma
11
+ ```
12
+
13
+ ## Run
14
+
15
+ ```
16
+ xenos-enigma --help
17
+ Usage: bin/run [--path FILE]
18
+ --help show help
19
+ --path FILE set path to radar data file, uses default data if no file provided
20
+ ```
21
+
22
+ ## Development
23
+
24
+ ```ruby
25
+ bundle install # install development dependencies
26
+ bin/xenos-enigma # run executable
27
+ rspec # run test suite
28
+ ```
29
+
30
+ ## Known xenos:
31
+ #### Tau
32
+ ~~~~
33
+ --o-----o--
34
+ ---o---o---
35
+ --ooooooo--
36
+ -oo-ooo-oo-
37
+ ooooooooooo
38
+ o-ooooooo-o
39
+ o-o-----o-o
40
+ ---oo-oo---
41
+ ~~~~
42
+ #### Aeldari
43
+ ~~~~
44
+ ---oo---
45
+ --oooo--
46
+ -oooooo-
47
+ oo-oo-oo
48
+ oooooooo
49
+ --o--o--
50
+ -o-oo-o-
51
+ o-o--o-o
52
+ ~~~~
53
+
54
+ #### Radar sample:
55
+ ~~~~
56
+ ----o--oo----o--ooo--ooo--o------o---oo-o----oo---o--o---------o----o------o-------------o--o--o--o-
57
+ --o-o-----oooooooo-oooooo---o---o----o------ooo-o---o--o----o------o--o---ooo-----o--oo-o------o----
58
+ --o--------oo-ooo-oo-oo-oo-----O------------ooooo-----oo----o------o---o--o--o-o-o------o----o-o-o--
59
+ -------o--oooooo--o-oo-o--o-o-----oo--o-o-oo--o-oo-oo-o--------o-----o------o-ooooo---o--o--o-------
60
+ ------o---o-ooo-ooo----o-----oo-------o---oo-ooooo-o------o----o--------o-oo--ooo-oo-------------o-o
61
+ -o--o-----o-o---o-ooooo-o-------oo---o---------o-----o-oo-----------oo----ooooooo-ooo-oo------------
62
+ o-------------ooooo-o--o--o--o-------o--o-oo-oo-o-o-o----oo------------o--oooo--ooo-o----o-----o--o-
63
+ --o-------------------------oo---------oo-o-o--ooo----oo----o--o--o----o--o-o-----o-o------o-o------
64
+ -------------------o----------o------o--o------o--------o--------o--oo-o-----oo-oo---o--o---o-----oo
65
+ ----------o----------o---o--------------o--o----o--o-o------------oo------o--o-o---o-----o----------
66
+ ------o----o-o---o-----o-o---o-----oo-o--------o---------------------------------o-o-o--o-----------
67
+ ---------------o-------o-----o-------o-------------------o-----o---------o-o-------------o-------oo-
68
+ -o--o-------------o-o-----o--o--o--oo-------------o----ooo----o-------------o----------oo----o---o-o
69
+ -o--o-------------o----oo------o--o-------o--o-----o-----o----o-----o--o----o--oo-----------o-------
70
+ -o-----oo-------o------o----o----------o--o----o-----o-----o-------o-----------o---o-o--oooooo-----o
71
+ -o--------o-----o-----o---------oo----oo---o-o---------o---o--oooo-oo--o-------o------oo--oo--o-----
72
+ ------------o---------o---------o----oooo-------------oo-oo-----ooo-oo-----o-------o-oo-oooooooo---o
73
+ ----------------------o------------oooooooo---o-----o-------o--oooooo-o------------o-o-ooooooo-o----
74
+ ------------o------o---o---o-------oo-oo--o--o---------o--o-o-o-ooooo-o--------------oo-o----o-oo-o-
75
+ ---o-o----------oo-------oo----o----oooooooo-------o----o-o-o-o-----o-o-----o----------ooo-oo--o---o
76
+ -o-o---------o-o---------------o--o--o--ooo---ooo-------o------oo-oo------------o--------o--o-o--o--
77
+ -------oo---------------------------o-oo----------o------o-o-------o-----o----o-----o-oo-o-----o---o
78
+ ---o--------o-----o-------o-oo-----oo--oo-o----oo----------o--o---oo------oo----o-----o-------o-----
79
+ ---o--ooo-o---------o-o----o------------o---------o----o--o-------o----o--------o----------------oo-
80
+ ---o------o----------------o----o------o------o---oo-----------o-------------o----------oo---------o
81
+ --oo---------------o--o------o---o-----o--o-------------o------o-------o-----o-----o----o------o--o-
82
+ -o-------o----------o-o-o-------o-----o--o-o-----------o-oo-----------o------o---------o-----o-o----
83
+ ----------o----o-------o----o--o------o------------o---o---------------oo----o-----ooo--------------
84
+ ----o--------oo----o-o----o--o------ooo----o-oooo---o--o-oo--------o-oo-----o-o---o-o--o-----oo-----
85
+ ------o--------o-ooooo----o---o--o-----o---------------o-o-------o-----o----------------------------
86
+ o-------oo----o--oooooo-o---o--o------oooo----------o-oo-------o---o----------o------oo-------------
87
+ -o---o----------o--oo-oo-o---o-----o-o-----------------------oo--o------o------o--------------------
88
+ -----oo-o-o-o---ooooooooo----o----o--------o--o---oo---o------------o----------o-o---o------o-o--oo-
89
+ ------o------o---ooo-o---------------------------o--o---o---o----o--o-------o-----o------o----o----o
90
+ -------o----------ooo-o-----o----o---o--o-oo--o--o-o--o------o--o-oo---ooo------------------------o-
91
+ -o-------o------o-o--ooo--o---o---oo-----o----o-------------o----o-ooo-o------o--o-o------o-o-------
92
+ ---oo--o---o-o---------o---o--------------o--o-----o-------o-----o--o---o-oo--------o----o----o-----
93
+ o------o----oo-o-----------oo--o---o--------o-o------o-------o-o------o-oo---------o-----oo---------
94
+ ----o--o---o-o-----------o---o------------o-------o----o--o--o--o-o---------------o-----------------
95
+ -------oo--o-o-----o-----o----o-o--o----------------------o-------o------o----oo----ooo---------o---
96
+ o-----oo-------------------o--o-----o-----------o------o-------o----o-----------o----------------o--
97
+ --o---o-------o------------o--------------------o----o--o-------------oo---o---------oo--------o----
98
+ --o--------o---------o------------o------o-------o------------o-------o---o---------ooooo-----------
99
+ ------o--------------o-o-o---------o---o-------o--o-----o-------o-o----------o-----oo-ooo----------o
100
+ --o---------------o----o--oo-------------o---------o-------------------oo---------oo-o-ooo----------
101
+ -o-----------o------ooo----o----------------ooo-----o--------o--o---o-----------o-o-oooooo--------oo
102
+ -o---o-------o---o-oooo-----o-------------------o----oo-----------------o--o--------o--o------o--o--
103
+ -------o---o------oooooo--o----ooo--o--------o-------o----------------------------oo-oo-o--o--------
104
+ o--oo------o-----oo--o-oo------------oo--o------o--o-------------oo----o------------oooo-o------oo--
105
+ -----o----------ooooooooo--------------oo--------------oo-----o-----o-o--o------o----------o----o---
106
+ ~~~~
107
+ #### Radar echo:
108
+ ~~~~
109
+ --------------------oooo---------------------oo-----------------------------------------------------
110
+ -------------------oooooo-------------------oooo----------------------------o-----o-----------------
111
+ ------------------oo-oo-oo-----------------oooooo----------------------------o---o------------------
112
+ ------------------oooooooo----------------oo-oo-oo--------------------------ooooooo-----------------
113
+ --------------------o--o------------------oooooooo-------------------------oo-ooo-oo----------------
114
+ -------------------o-oo-o-------------------o--o--------------------------ooooooooooo---------------
115
+ ------------------o-o--o-o-----------------o-oo-o-------------------------o-ooooooo-o---------------
116
+ ------------------------------------------o-o--o-o------------------------o-o-----o-o---------------
117
+ -----------------------------------------------------------------------------oo-oo------------------
118
+ ----------------------------------------------------------------------------------------------------
119
+ ----------------------------------------------------------------------------------------------------
120
+ ----------------------------------------------------------------------------------------------------
121
+ ---------------------------------------------------------------------------------------o-----o------
122
+ --------------------------------------------------------------o-----o-------------------o---o-------
123
+ ---------------------------------------------------------------o---o-------------------ooooooo------
124
+ --------------------------------------oo----------------------ooooooo-----------------oo-ooo-oo-----
125
+ -------------------------------------oooo--------------------oo-ooo-oo---------------ooooooooooo----
126
+ ------------------------------------oooooo------------------ooooooooooo--------------o-ooooooo-o----
127
+ -----------------------------------oo-oo-oo-----------------o-ooooooo-o--------------o-o-----o-o----
128
+ -----------------------------------oooooooo-----------------o-o-----o-o-----------------oo-oo-------
129
+ -------------------------------------o--o----------------------oo-oo--------------------------------
130
+ ------------------------------------o-oo-o----------------------------------------------------------
131
+ -----------------------------------o-o--o-o---------------------------------------------------------
132
+ ----------------------------------------------------------------------------------------------------
133
+ ----------------------------------------------------------------------------------------------------
134
+ ----------------------------------------------------------------------------------------------------
135
+ ----------------------------------------------------------------------------------------------------
136
+ ----------------------------------------------------------------------------------------------------
137
+ -------------------oo-------------------------------------------------------------------------------
138
+ ------------------oooo------------------------------------------------------------------------------
139
+ -----------------oooooo-----------------------------------------------------------------------------
140
+ ----------------oo-oo-oo----------------------------------------------------------------------------
141
+ ----------------oooooooo----------------------------------------------------------------------------
142
+ ------------------o--o------------------------------------------------------------------------------
143
+ -----------------o-oo-o-----------------------------------------------------------------------------
144
+ ----------------o-o--o-o----------------------------------------------------------------------------
145
+ ----------------------------------------------------------------------------------------------------
146
+ ----------------------------------------------------------------------------------------------------
147
+ ----------------------------------------------------------------------------------------------------
148
+ ----------------------------------------------------------------------------------------------------
149
+ ----------------------------------------------------------------------------------------------------
150
+ -------------------------------------------------------------------------------------oo-------------
151
+ ------------------------------------------------------------------------------------oooo------------
152
+ -----------------------------------------------------------------------------------oooooo-----------
153
+ ----------------------------------------------------------------------------------oo-oo-oo----------
154
+ --------------------oo------------------------------------------------------------oooooooo----------
155
+ -------------------oooo-------------------------------------------------------------o--o------------
156
+ ------------------oooooo-----------------------------------------------------------o-oo-o-----------
157
+ -----------------oo-oo-oo---------------------------------------------------------o-o--o-o----------
158
+ -----------------oooooooo---------------------------------------------------------------------------
159
+ ~~~~
data/bin/xenos-enigma ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/xenos_enigma'
3
+
4
+ XenosEnigma::Runner.start
@@ -0,0 +1 @@
1
+ require_relative 'xenos_enigma/runner'
@@ -0,0 +1,56 @@
1
+ module XenosEnigma
2
+ module DefaultData
3
+ SCAN_DATA = <<~DATA.freeze
4
+ ----o--oo----o--ooo--ooo--o------o---oo-o----oo---o--o---------o----o------o-------------o--o--o--o-
5
+ --o-o-----oooooooo-oooooo---o---o----o------ooo-o---o--o----o------o--o---ooo-----o--oo-o------o----
6
+ --o--------oo-ooo-oo-oo-oo-----O------------ooooo-----oo----o------o---o--o--o-o-o------o----o-o-o--
7
+ -------o--oooooo--o-oo-o--o-o-----oo--o-o-oo--o-oo-oo-o--------o-----o------o-ooooo---o--o--o-------
8
+ ------o---o-ooo-ooo----o-----oo-------o---oo-ooooo-o------o----o--------o-oo--ooo-oo-------------o-o
9
+ -o--o-----o-o---o-ooooo-o-------oo---o---------o-----o-oo-----------oo----ooooooo-ooo-oo------------
10
+ o-------------ooooo-o--o--o--o-------o--o-oo-oo-o-o-o----oo------------o--oooo--ooo-o----o-----o--o-
11
+ --o-------------------------oo---------oo-o-o--ooo----oo----o--o--o----o--o-o-----o-o------o-o------
12
+ -------------------o----------o------o--o------o--------o--------o--oo-o-----oo-oo---o--o---o-----oo
13
+ ----------o----------o---o--------------o--o----o--o-o------------oo------o--o-o---o-----o----------
14
+ ------o----o-o---o-----o-o---o-----oo-o--------o---------------------------------o-o-o--o-----------
15
+ ---------------o-------o-----o-------o-------------------o-----o---------o-o-------------o-------oo-
16
+ -o--o-------------o-o-----o--o--o--oo-------------o----ooo----o-------------o----------oo----o---o-o
17
+ -o--o-------------o----oo------o--o-------o--o-----o-----o----o-----o--o----o--oo-----------o-------
18
+ -o-----oo-------o------o----o----------o--o----o-----o-----o-------o-----------o---o-o--oooooo-----o
19
+ -o--------o-----o-----o---------oo----oo---o-o---------o---o--oooo-oo--o-------o------oo--oo--o-----
20
+ ------------o---------o---------o----oooo-------------oo-oo-----ooo-oo-----o-------o-oo-oooooooo---o
21
+ ----------------------o------------oooooooo---o-----o-------o--oooooo-o------------o-o-ooooooo-o----
22
+ ------------o------o---o---o-------oo-oo--o--o---------o--o-o-o-ooooo-o--------------oo-o----o-oo-o-
23
+ ---o-o----------oo-------oo----o----oooooooo-------o----o-o-o-o-----o-o-----o----------ooo-oo--o---o
24
+ -o-o---------o-o---------------o--o--o--ooo---ooo-------o------oo-oo------------o--------o--o-o--o--
25
+ -------oo---------------------------o-oo----------o------o-o-------o-----o----o-----o-oo-o-----o---o
26
+ ---o--------o-----o-------o-oo-----oo--oo-o----oo----------o--o---oo------oo----o-----o-------o-----
27
+ ---o--ooo-o---------o-o----o------------o---------o----o--o-------o----o--------o----------------oo-
28
+ ---o------o----------------o----o------o------o---oo-----------o-------------o----------oo---------o
29
+ --oo---------------o--o------o---o-----o--o-------------o------o-------o-----o-----o----o------o--o-
30
+ -o-------o----------o-o-o-------o-----o--o-o-----------o-oo-----------o------o---------o-----o-o----
31
+ ----------o----o-------o----o--o------o------------o---o---------------oo----o-----ooo--------------
32
+ ----o--------oo----o-o----o--o------ooo----o-oooo---o--o-oo--------o-oo-----o-o---o-o--o-----oo-----
33
+ ------o--------o-ooooo----o---o--o-----o---------------o-o-------o-----o----------------------------
34
+ o-------oo----o--oooooo-o---o--o------oooo----------o-oo-------o---o----------o------oo-------------
35
+ -o---o----------o--oo-oo-o---o-----o-o-----------------------oo--o------o------o--------------------
36
+ -----oo-o-o-o---ooooooooo----o----o--------o--o---oo---o------------o----------o-o---o------o-o--oo-
37
+ ------o------o---ooo-o---------------------------o--o---o---o----o--o-------o-----o------o----o----o
38
+ -------o----------ooo-o-----o----o---o--o-oo--o--o-o--o------o--o-oo---ooo------------------------o-
39
+ -o-------o------o-o--ooo--o---o---oo-----o----o-------------o----o-ooo-o------o--o-o------o-o-------
40
+ ---oo--o---o-o---------o---o--------------o--o-----o-------o-----o--o---o-oo--------o----o----o-----
41
+ o------o----oo-o-----------oo--o---o--------o-o------o-------o-o------o-oo---------o-----oo---------
42
+ ----o--o---o-o-----------o---o------------o-------o----o--o--o--o-o---------------o-----------------
43
+ -------oo--o-o-----o-----o----o-o--o----------------------o-------o------o----oo----ooo---------o---
44
+ o-----oo-------------------o--o-----o-----------o------o-------o----o-----------o----------------o--
45
+ --o---o-------o------------o--------------------o----o--o-------------oo---o---------oo--------o----
46
+ --o--------o---------o------------o------o-------o------------o-------o---o---------ooooo-----------
47
+ ------o--------------o-o-o---------o---o-------o--o-----o-------o-o----------o-----oo-ooo----------o
48
+ --o---------------o----o--oo-------------o---------o-------------------oo---------oo-o-ooo----------
49
+ -o-----------o------ooo----o----------------ooo-----o--------o--o---o-----------o-o-oooooo--------oo
50
+ -o---o-------o---o-oooo-----o-------------------o----oo-----------------o--o--------o--o------o--o--
51
+ -------o---o------oooooo--o----ooo--o--------o-------o----------------------------oo-oo-o--o--------
52
+ o--oo------o-----oo--o-oo------------oo--o------o--o-------------oo----o------------oooo-o------oo--
53
+ -----o----------ooooooooo--------------oo--------------oo-----o-----o-o--o------o----------o----o---
54
+ DATA
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ module XenosEnigma
2
+ # HitCollector will consume all ship hits, and cache their data and coordinates
3
+ class HitCollector
4
+ def initialize
5
+ @hit_matrix_cache = {}
6
+ end
7
+
8
+ def detection_data(position_x, position_y)
9
+ get_cache(position_x, position_y)
10
+ end
11
+
12
+ def already_detected?(position_x, position_y)
13
+ !get_cache(position_x, position_y).nil?
14
+ end
15
+
16
+ def push(xeno_hit, scan_position_x, scan_position_y)
17
+ xeno_hit.radar_x_position = scan_position_x
18
+ xeno_hit.radar_y_position = scan_position_y
19
+
20
+ consume(xeno_hit)
21
+ end
22
+
23
+ private
24
+
25
+ def consume(xeno_hit)
26
+ xeno_data = xeno_hit.xeno_instance.xeno_signature
27
+
28
+ xeno_data.each_with_index do |xeno_row_data, xeno_y|
29
+ next if xeno_y < xeno_hit.xeno_y_start
30
+
31
+ xeno_row = xeno_row_data.split(//)
32
+ xeno_row.each_with_index do |xeno_char, xeno_x|
33
+ global_x = xeno_hit.radar_x_position + xeno_x
34
+ global_y = xeno_hit.radar_y_position + xeno_y - xeno_hit.xeno_y_start
35
+
36
+ @hit_matrix_cache[cache_key(global_x, global_y)] = xeno_char
37
+ end
38
+ end
39
+ end
40
+
41
+ def get_cache(position_x, position_y)
42
+ @hit_matrix_cache[cache_key(position_x, position_y)]
43
+ end
44
+
45
+ def cache_key(position_x, position_y)
46
+ "#{position_x}::#{position_y}"
47
+ end
48
+ end
49
+
50
+ # Hit is placeholder for ship detection information
51
+ class Hit
52
+ attr_accessor :radar_x_position,
53
+ :radar_y_position,
54
+ :xeno_instance,
55
+ :xeno_y_start
56
+
57
+ def initialize(xeno_instance, xeno_y_start = 0)
58
+ @xeno_instance = xeno_instance
59
+ @xeno_y_start = xeno_y_start
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,67 @@
1
+ require_relative 'default_data'
2
+ require_relative 'hit_collector'
3
+ Dir[File.expand_path('xenos/*.rb', File.dirname(__FILE__))].each do |file|
4
+ require_relative file
5
+ end
6
+
7
+ module XenosEnigma
8
+ # Radar will orchestrate scan of radar data, xenos detection and echo of those findings
9
+ class Radar
10
+ include DefaultData
11
+
12
+ def initialize(data = nil)
13
+ @data = (data || SCAN_DATA).split(/\n/)
14
+ @known_xenos = load_all_known_xenos
15
+ @hit_collector = XenosEnigma::HitCollector.new
16
+ end
17
+
18
+ def scan
19
+ @data.each_with_index do |scan_row, scan_position_y|
20
+ (0..scan_row.length).each do |scan_position_x|
21
+ next if @hit_collector.already_detected?(scan_position_x, scan_position_y)
22
+
23
+ @known_xenos.each do |xeno|
24
+ partial = scan_row_partial(scan_row, scan_position_x, xeno.ship_width)
25
+ hit = xeno.analyze?(partial, look_ahead_data(scan_position_x, scan_position_y, xeno))
26
+ @hit_collector.push(hit, scan_position_x, scan_position_y) if hit
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def echo
33
+ echo_width = @data.first.size - 1
34
+ echo_height = @data.size - 1
35
+ print "\033[33m"
36
+
37
+ (0..echo_height).each do |echo_y|
38
+ (0..echo_width).each do |echo_x|
39
+ hit_data = @hit_collector.detection_data(echo_x, echo_y)
40
+ print hit_data || '-'
41
+ end
42
+ puts
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def look_ahead_data(position_x, position_y, xeno)
49
+ results = []
50
+ scan_to_y = [(position_y + xeno.ship_height), @data.size].min - 1
51
+ (position_y..scan_to_y).each do |scan_y|
52
+ partial = scan_row_partial(@data[scan_y], position_x, xeno.ship_width)
53
+ results.push(partial)
54
+ end
55
+ results
56
+ end
57
+
58
+ def scan_row_partial(scan_row, position_x, x_offset)
59
+ scan_row[position_x..(position_x + x_offset - 1)]
60
+ end
61
+
62
+ def load_all_known_xenos
63
+ xenox_clazzes = XenosEnigma::Xenos.constants.reject { |xc| xc.downcase.eql?(:base) }
64
+ xenox_clazzes.collect { |xc| XenosEnigma::Xenos.const_get(xc).new }
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'radar'
2
+ require 'getoptlong'
3
+
4
+ module XenosEnigma
5
+ # Runner is responsable for end user interaction via console
6
+ class Runner
7
+ def self.start
8
+ radar_data = nil
9
+
10
+ opts = GetoptLong.new(['--help', '-h', GetoptLong::NO_ARGUMENT], ['--path', GetoptLong::OPTIONAL_ARGUMENT])
11
+ opts = Hash[*opts.get_option]
12
+
13
+ if opts['--help']
14
+ puts 'Usage: bin/run [--path FILE]'
15
+ puts "--help \t show help"
16
+ puts "--path FILE \t set path to radar data file, uses default data if no file provided"
17
+ exit(0)
18
+ end
19
+
20
+ if (file_provided = opts['--path'])
21
+ unless File.exist?(file_provided)
22
+ warn "File #{file_provided} not found"
23
+ exit(1)
24
+ end
25
+
26
+ radar_data = File.read(file_provided)
27
+ end
28
+
29
+ radar = XenosEnigma::Radar.new(radar_data)
30
+ radar.scan
31
+ radar.echo
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module XenosEnigma
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+
3
+ module XenosEnigma
4
+ module Xenos
5
+ class Aeldari < Base
6
+ SIGNATURE = <<~SIGNATURE.freeze
7
+ --o-----o--
8
+ ---o---o---
9
+ --ooooooo--
10
+ -oo-ooo-oo-
11
+ ooooooooooo
12
+ o-ooooooo-o
13
+ o-o-----o-o
14
+ ---oo-oo---
15
+ SIGNATURE
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ module XenosEnigma
2
+ module Xenos
3
+ # Base class holds all the logic for correctly detecting xenos ships
4
+ class Base
5
+ attr_reader :ship_width, :ship_height, :xeno_signature
6
+
7
+ SHIP_DETECTION = { tolerance: 2, compound_tolerance: 1.6, min_segments: 3 }.freeze
8
+
9
+ def initialize
10
+ raise 'This is an abstract class' if instance_of?(XenosEnigma::Xenos::Base)
11
+
12
+ @xeno_signature = self.class::SIGNATURE.split(/\n/)
13
+ @ship_width = @xeno_signature.first.size
14
+ @ship_height = @xeno_signature.size
15
+ end
16
+
17
+ def analyze?(radar_partial, look_ahead_radar_data)
18
+ return if radar_partial.size < ship_width
19
+
20
+ hit = nil
21
+
22
+ @xeno_signature.each_with_index do |xeno_row, xeno_y_position|
23
+ if possible_match?(xeno_row, radar_partial)
24
+ hit = full_scan(xeno_y_position, look_ahead_radar_data)
25
+ break if hit
26
+ end
27
+ end
28
+
29
+ hit
30
+ end
31
+
32
+ private
33
+
34
+ def full_scan(xeno_y_position, look_ahead_radar_data)
35
+ compound_match_score = 0
36
+ lines_scanned = 0
37
+
38
+ (xeno_y_position..(ship_height - 1)).each do |y_scan|
39
+ break if look_ahead_radar_data[y_scan - xeno_y_position].nil?
40
+
41
+ compound_match_score += pattern_difference(@xeno_signature[y_scan], look_ahead_radar_data[y_scan - xeno_y_position])
42
+ lines_scanned += 1
43
+ end
44
+
45
+ is_detection_cofirmed = (lines_scanned * SHIP_DETECTION[:compound_tolerance] >= compound_match_score)
46
+
47
+ if lines_scanned >= SHIP_DETECTION[:min_segments] && is_detection_cofirmed
48
+ XenosEnigma::Hit.new(self, xeno_y_position)
49
+ end
50
+ end
51
+
52
+ def possible_match?(xeno_row, radar_partial)
53
+ pattern_difference(xeno_row, radar_partial) <= SHIP_DETECTION[:tolerance]
54
+ end
55
+
56
+ def pattern_difference(data1, data2)
57
+ DidYouMean::Levenshtein.distance(data1, data2)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+
3
+ module XenosEnigma
4
+ module Xenos
5
+ class Tau < Base
6
+ SIGNATURE = <<~SIGNATURE.freeze
7
+ ---oo---
8
+ --oooo--
9
+ -oooooo-
10
+ oo-oo-oo
11
+ oooooooo
12
+ --o--o--
13
+ -o-oo-o-
14
+ o-o--o-o
15
+ SIGNATURE
16
+ end
17
+ end
18
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,61 @@
1
+ require_relative '../lib/xenos_enigma/xenos/tau'
2
+ require_relative 'helpers/randomize'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Randomize
6
+ end
7
+
8
+ RSpec.describe XenosEnigma::Xenos::Base do
9
+ describe 'instance methods' do
10
+ let(:tau) { XenosEnigma::Xenos::Tau.new }
11
+ let(:data) { tau.xeno_signature }
12
+
13
+ it 'should not allow class to be instanced' do
14
+ expect { XenosEnigma::Xenos::Base.new }.to raise_exception(RuntimeError, 'This is an abstract class')
15
+ end
16
+
17
+ it 'should match pattern as a possible match' do
18
+ expect(tau.send(:possible_match?, '---oo---', '---o-o--')).to be true
19
+ expect(tau.send(:possible_match?, '---oo---', '--o-o---')).to be true
20
+ end
21
+
22
+ it 'should not match pattern as a possible match' do
23
+ expect(tau.send(:possible_match?, '---oo---', '-----o--')).to be false
24
+ expect(tau.send(:possible_match?, '---oo---', '--o-----')).to be false
25
+ end
26
+
27
+ it 'should analyze data correctly and return exact hit' do
28
+ rdata = randomize_data(data)
29
+ result = tau.analyze?(rdata[0], rdata)
30
+
31
+ expect(result).to be_a XenosEnigma::Hit
32
+ expect(result.xeno_y_start).to eq 0
33
+ end
34
+
35
+ it 'should analyze data correctly and return partial hit from top of the ship' do
36
+ rdata = randomize_data(data.take(5))
37
+ result = tau.analyze?(rdata[0], rdata)
38
+
39
+ expect(result).to be_a XenosEnigma::Hit
40
+ expect(result.xeno_y_start).to eq 0
41
+ end
42
+
43
+ it 'should analyze data correctly and return partial hit from bottom of the ship' do
44
+ ship_segments = 4
45
+
46
+ partial_data = data.dup[ship_segments..-1]
47
+ rdata = randomize_data(partial_data)
48
+ result = tau.analyze?(rdata[0], rdata)
49
+
50
+ expect(result).to be_a XenosEnigma::Hit
51
+ expect(result.xeno_y_start).to eq ship_segments
52
+ end
53
+
54
+ it 'should analyze data and not return a hit' do
55
+ rdata = randomize_data(['--------'] * 8)
56
+ result = tau.analyze?(rdata[0], rdata)
57
+
58
+ expect(result).to be_falsey
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module Randomize
2
+ def randomize_data(data)
3
+ data.collect do |row|
4
+ random_index = rand(0..(row.size - 1))
5
+ row[random_index] = 'o'.eql?(row[random_index]) ? '-' : 'o'
6
+ row
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../lib/xenos_enigma/hit_collector'
2
+ require_relative '../lib/xenos_enigma/xenos/aeldari'
3
+
4
+ RSpec.describe XenosEnigma::HitCollector do
5
+ describe 'instance methods' do
6
+ subject { XenosEnigma::HitCollector.new }
7
+ let(:aeldari) { XenosEnigma::Xenos::Aeldari.new }
8
+
9
+ before(:each) do
10
+ @hit = XenosEnigma::Hit.new(aeldari)
11
+ subject.push(@hit, 0, 0)
12
+ end
13
+
14
+ it 'should correctly process a new hit and each coordinate' do
15
+ (0..aeldari.ship_width - 1).each do |position_x|
16
+ (0..aeldari.ship_height - 1).each do |position_y|
17
+ expect(subject.already_detected?(position_x, position_y)).to be true
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'should correctly process a new hit and mark each ship segment' do
23
+ scanned_data = []
24
+ (0..aeldari.ship_width - 1).each do |position_x|
25
+ (0..aeldari.ship_height - 1).each do |position_y|
26
+ scanned_data << subject.detection_data(position_x, position_y)
27
+ end
28
+ end
29
+
30
+ ship_signiture = @hit.xeno_instance.xeno_signature.collect do |row|
31
+ row.split(//)
32
+ end.flatten.sort
33
+
34
+ expect(ship_signiture).to eq scanned_data.sort
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../lib/xenos_enigma/radar'
2
+
3
+ RSpec.describe XenosEnigma::Radar do
4
+ describe 'when working with default data' do
5
+ subject { XenosEnigma::Radar.new }
6
+ let(:tau) { XenosEnigma::Xenos::Tau.new }
7
+ let(:expected_data) do
8
+ <<~DATA.freeze
9
+ ----o--o
10
+ --o-o---
11
+ --o-----
12
+ -------o
13
+ ------o-
14
+ -o--o---
15
+ o-------
16
+ --o-----
17
+ DATA
18
+ end
19
+
20
+ it 'collect all known xenos' do
21
+ xenos = subject.instance_variable_get(:@known_xenos)
22
+
23
+ expect(xenos).to be_a Array
24
+ expect(xenos).not_to be_empty
25
+ end
26
+
27
+ it 'make sure that each xenos is analyzed' do
28
+ expect_any_instance_of(XenosEnigma::Xenos::Tau).to receive(:analyze?).at_least(:once)
29
+ expect_any_instance_of(XenosEnigma::Xenos::Aeldari).to receive(:analyze?).at_least(:once)
30
+
31
+ subject.scan
32
+ end
33
+
34
+ it 'returns correct scan ahead data' do
35
+ data = subject.send(:look_ahead_data, 0, 0, tau)
36
+
37
+ expect(data).to eq expected_data.split(/\n/)
38
+ expect(data.first.size).to eq(tau.ship_width)
39
+ expect(data.size).to eq(tau.ship_height)
40
+ end
41
+
42
+ it 'returns correct data partial' do
43
+ data = subject.send(:scan_row_partial, expected_data, 0, 5)
44
+ expect(data).to eq '----o'
45
+ end
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xenos-enigma
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Haris Krajina
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.1.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.1.0
27
+ description:
28
+ email:
29
+ - haris.krajina@gmail.com
30
+ executables:
31
+ - xenos-enigma
32
+ extensions: []
33
+ extra_rdoc_files:
34
+ - CHANGELOG
35
+ - LICENSE
36
+ - README.md
37
+ files:
38
+ - CHANGELOG
39
+ - LICENSE
40
+ - README.md
41
+ - bin/xenos-enigma
42
+ - lib/xenos_enigma.rb
43
+ - lib/xenos_enigma/default_data.rb
44
+ - lib/xenos_enigma/hit_collector.rb
45
+ - lib/xenos_enigma/radar.rb
46
+ - lib/xenos_enigma/runner.rb
47
+ - lib/xenos_enigma/version.rb
48
+ - lib/xenos_enigma/xenos/aeldari.rb
49
+ - lib/xenos_enigma/xenos/base.rb
50
+ - lib/xenos_enigma/xenos/tau.rb
51
+ - spec/base_spec.rb
52
+ - spec/helpers/randomize.rb
53
+ - spec/hit_collector_spec.rb
54
+ - spec/radar_spec.rb
55
+ homepage: http://github.com/hkraji/xenos-enigma
56
+ licenses:
57
+ - MIT
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '2.4'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.1.2
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Space invaders radar set in the world of 40k
78
+ test_files:
79
+ - spec/hit_collector_spec.rb
80
+ - spec/radar_spec.rb
81
+ - spec/helpers/randomize.rb
82
+ - spec/base_spec.rb