pitchcar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (9) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +22 -0
  3. data/Readme.md +42 -0
  4. data/boyermoore.rb +117 -0
  5. data/finder.rb +50 -0
  6. data/piece.rb +66 -0
  7. data/pitchcar_tracks +20160 -0
  8. data/track.rb +99 -0
  9. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0b54b3e0e398d6dee15a2c15379efa23ced9a327
4
+ data.tar.gz: 5b0c6ecfefc77fb815fd33b140a27f906f8922f4
5
+ SHA512:
6
+ metadata.gz: 45e2206b61b7a56e40784fe507d60a71759393a73a5204e16ed6bf86a2cb2be1241a1bf1cf9e7ff6653090b5aa91da2d18bf784d5b338af3e0703b297e8851e9
7
+ data.tar.gz: 6de830f9c750dd966cb53dd391747b579a33055e60d4279f6f84e8e69c72dff8d9b45cc720405e2d37211c19be5c11d1b3725744cd753337e1f02f6890e8c835
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Jonah Hirsch
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.
22
+
data/Readme.md ADDED
@@ -0,0 +1,42 @@
1
+ # PitchCar Track Generator
2
+
3
+ Generates all possible tracks, or a single random valid track, for
4
+ [PitchCar](https://boardgamegeek.com/boardgame/150/pitchcar)
5
+
6
+ ## Rules:
7
+ * Tracks will always begin with a straight piece
8
+ * Tracks must not overlap
9
+ * Tracks must form a complete look
10
+ * Track must not already have been found as a rotation
11
+
12
+ All possible values for a basic PitchCar set (10 curves, 6 straight pieces)
13
+ are provided in `pitchcar_tracks`
14
+ * `Slw` = Straight piece with left wall
15
+ * `Srw` = Straight piece with right wall
16
+ * `R` = Right Turn
17
+ * `L` = Left Turn
18
+
19
+ ## Usage:
20
+ ### Generate all possible tracks
21
+ ```ruby
22
+ require_relative 'finder.rb'
23
+ # tracks = Pitchcar::Finder.find_all_tracks(STRAIGHT_PIECES, LEFT_RIGHT_PIECES)
24
+ tracks = Pitchcar::Finder.find_all_tracks(6, 10)
25
+ tracks.first.to_s # => 'Slw Slw Slw Slw L Slw L Slw L R R L L R L L'
26
+ ```
27
+
28
+ ### Find a random valid track
29
+ ```ruby
30
+ require_relative 'finder.rb'
31
+ # track = Pitchcar::Finder.random_valid_track(STRAIGHT_PIECES, LEFT_RIGHT_PIECES)
32
+ track = Pitchcar::Finder.random_valid_track(6, 10)
33
+ track.to_s # => 'Slw Slw L Slw R R Slw Slw R L R R L Slw R R'
34
+ ```
35
+
36
+ ### Find `n` random valid tracks
37
+ ```ruby
38
+ require_relative 'finder.rb'
39
+ # tracks = Pitchcar::Finder.random_valid_tracks(STRAIGHT_PIECES, LEFT_RIGHT_PIECES, COUNT)
40
+ track = Pitchcar::Finder.random_valid_tracks(6, 10, 4)
41
+ track.map(&:to_s) # => ["Slw Srw L R Srw L L Srw R L L Slw R Slw L L", "Slw L Slw L Slw R Srw L L R L Srw L R L Slw", "Slw R Slw Slw R Slw Srw L R R Srw R L R L R", "Slw Slw L Srw L R L Srw R L L Srw L R Slw L"]
42
+ ```
data/boyermoore.rb ADDED
@@ -0,0 +1,117 @@
1
+ # From https://github.com/jashmenn/boyermoore
2
+
3
+ # Hash impersonator that accepts regular expressions as keys. But the regular
4
+ # expression lookups are slow, so don't use them (unless you have to).
5
+ class RichHash
6
+ def initialize
7
+ @regexps = {}
8
+ @regular = {}
9
+ end
10
+
11
+ def [](k)
12
+ regular = @regular[k]
13
+ return regular if regular
14
+ if @regexps.size > 0
15
+ @regexps.each do |regex,v| # linear search is going to be slow
16
+ return v if regex.match(k)
17
+ end
18
+ end
19
+ nil
20
+ end
21
+
22
+ def []=(k,v)
23
+ if k.kind_of?(Regexp)
24
+ @regexps[k] = v
25
+ else
26
+ @regular[k] = v
27
+ end
28
+ end
29
+ end
30
+
31
+ # ported directly from this version wikipedia:
32
+ # http://en.wikipedia.org/w/index.php?title=Boyer%E2%80%93Moore_string_search_algorithm&diff=391986850&oldid=391398281
33
+ # it's not very rubyish but it works
34
+ module BoyerMoore
35
+
36
+ def self.compute_prefix(str)
37
+ size = str.length
38
+ k = 0
39
+ result = [0]
40
+ 1.upto(size - 1) do |q|
41
+ while (k > 0) && (str[k] != str[q])
42
+ k = result[k-1]
43
+ end
44
+ k += 1 if(str[k] == str[q])
45
+ result[q] = k
46
+ end
47
+ result
48
+ end
49
+
50
+ def self.prepare_badcharacter_heuristic(str)
51
+ result = RichHash.new
52
+ 0.upto(str.length - 1) do |i|
53
+ result[str[i]] = i
54
+ end
55
+ result
56
+ end
57
+
58
+ def self.prepare_goodsuffix_heuristic(normal)
59
+ size = normal.size
60
+ result = []
61
+
62
+ reversed = normal.dup.reverse
63
+ prefix_normal = compute_prefix(normal)
64
+ prefix_reversed = compute_prefix(reversed)
65
+
66
+ 0.upto(size) do |i|
67
+ result[i] = size - prefix_normal[size-1]
68
+ end
69
+
70
+ 0.upto(size-1) do |i|
71
+ j = size - prefix_reversed[i]
72
+ k = i - prefix_reversed[i]+1
73
+ result[j] = k if result[j] > k
74
+ end
75
+ result
76
+ end
77
+
78
+ def self.search(haystack, needle)
79
+ needle_len = needle.size
80
+ haystack_len = haystack.size
81
+
82
+ return nil if haystack_len == 0
83
+ return haystack if needle_len == 0
84
+
85
+ badcharacter = self.prepare_badcharacter_heuristic(needle)
86
+ goodsuffix = self.prepare_goodsuffix_heuristic(needle)
87
+
88
+ s = 0
89
+ while s <= haystack_len - needle_len
90
+ j = needle_len
91
+ while (j > 0) && self.needle_matches?(needle[j-1], haystack[s+j-1])
92
+ j -= 1
93
+ end
94
+
95
+ if(j > 0)
96
+ k = badcharacter[haystack[s+j-1]]
97
+ k = -1 unless k
98
+ if (k < j) && (m = j-k-1) > goodsuffix[j]
99
+ s += m
100
+ else
101
+ s += goodsuffix[j]
102
+ end
103
+ else
104
+ return s
105
+ end
106
+ end
107
+ return nil
108
+ end
109
+
110
+ def self.needle_matches?(needle, haystack)
111
+ if needle.kind_of?(Regexp)
112
+ needle.match(haystack) ? true : false
113
+ else
114
+ needle == haystack
115
+ end
116
+ end
117
+ end
data/finder.rb ADDED
@@ -0,0 +1,50 @@
1
+ require_relative 'piece'
2
+ require_relative 'track'
3
+
4
+ module Pitchcar
5
+ class Finder
6
+
7
+ class << self
8
+ def find_all_tracks(straight, left_right)
9
+ tracks = find_tracks(straight - 1, left_right, 'S', [])
10
+ tracks.map(&:with_wall_combinations).flatten
11
+ end
12
+
13
+ def random_valid_track(straight, left_right)
14
+ track = nil
15
+ track = random_track(straight, left_right) until !track.nil? && track.valid?
16
+ track.with_wall_combinations.sample
17
+ end
18
+
19
+ def random_valid_tracks(straight, left_right, count = 2)
20
+ tracks = []
21
+ count.times { tracks << random_valid_track(straight, left_right) }
22
+ tracks
23
+ end
24
+
25
+ private
26
+
27
+ def find_tracks(straight, left_right, track_pieces, tracks)
28
+ print "Found #{tracks.size} tracks\r"
29
+ track = Track.build_from(track_pieces)
30
+ return false if track.overlaps?
31
+
32
+ return tracks << track if straight == 0 && left_right == 0 && track.valid?(tracks)
33
+ [track_pieces].product((['S'] * straight + ['L'] * left_right + ['R'] * left_right).uniq).each do |result|
34
+ if result[1] == 'S'
35
+ find_tracks(straight - 1, left_right, result.join, tracks)
36
+ else
37
+ find_tracks(straight, left_right - 1, result.join, tracks)
38
+ end
39
+ end
40
+ tracks
41
+ end
42
+
43
+ def random_track(straight, left_right)
44
+ left = Random.rand(1..left_right)
45
+ right = left_right - left
46
+ Track.build_from("S#{'S' * (straight - 1)}#{'L' * left}#{'R' * right}".split('').shuffle.join)
47
+ end
48
+ end
49
+ end
50
+ end
data/piece.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'json'
2
+
3
+ module Pitchcar
4
+ class Piece
5
+ TYPES = { STRAIGHT: 0, LEFT: 1, RIGHT: 2, STRAIGHT_LEFT_WALL: 3, STRAIGHT_RIGHT_WALL: 4 }
6
+ DIRECTIONS = { NORTH: 0, EAST: 1, WEST: 2, SOUTH: 3 }
7
+ attr_accessor :direction, :x, :y, :type
8
+
9
+ def self.starting_piece
10
+ piece = new
11
+ piece.x = 0
12
+ piece.y = 0
13
+ piece.type = TYPES[:STRAIGHT]
14
+ piece.direction = DIRECTIONS[:SOUTH]
15
+ piece
16
+ end
17
+
18
+ def self.type_from_string(string)
19
+ case string
20
+ when 'S'
21
+ TYPES[:STRAIGHT]
22
+ when 'L'
23
+ TYPES[:LEFT]
24
+ when 'R'
25
+ TYPES[:RIGHT]
26
+ end
27
+ end
28
+
29
+ def next_direction(from)
30
+ case type
31
+ when TYPES[:LEFT]
32
+ case from
33
+ when DIRECTIONS[:NORTH]
34
+ DIRECTIONS[:WEST]
35
+ when DIRECTIONS[:EAST]
36
+ DIRECTIONS[:NORTH]
37
+ when DIRECTIONS[:WEST]
38
+ DIRECTIONS[:SOUTH]
39
+ when DIRECTIONS[:SOUTH]
40
+ DIRECTIONS[:EAST]
41
+ end
42
+ when TYPES[:RIGHT]
43
+ case from
44
+ when DIRECTIONS[:NORTH]
45
+ DIRECTIONS[:EAST]
46
+ when DIRECTIONS[:EAST]
47
+ DIRECTIONS[:SOUTH]
48
+ when DIRECTIONS[:WEST]
49
+ DIRECTIONS[:NORTH]
50
+ when DIRECTIONS[:SOUTH]
51
+ DIRECTIONS[:WEST]
52
+ end
53
+ else
54
+ from
55
+ end
56
+ end
57
+
58
+ def to_s
59
+ TYPES.key(type).to_s.split('_').map { |word| word[0] }.join.downcase.capitalize
60
+ end
61
+
62
+ def to_h
63
+ { x: x, y: y, type: TYPES.key(type).downcase, direction: DIRECTIONS.key(direction).downcase }
64
+ end
65
+ end
66
+ end