cem 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7d56d8ba91aab41f5e12070a9f821516b8f69d626d3ffd7261dca402c41768ba
4
+ data.tar.gz: 513114a63b34c7375339e4eb17d931bc7925bb068495c4726a6577e98c0ebc7a
5
+ SHA512:
6
+ metadata.gz: 55106e14c662cec3b4ffc68c6e0bb5775405fadbb61d1fd2d4611becd38d7cf55445e8a7872107797df5aaa4da18656419dbf0ed37eb676dc10a2184aa7168f1
7
+ data.tar.gz: f8f56d39752badb44074162e20452814937bd40ee44bd0e22ea777bd775f5c830e25768ec21ca104310efa84bb3679e868d99af9a674a907807e479efc43cdf8
@@ -0,0 +1,57 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ .rspec_status
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ # Ignore Byebug command history file.
18
+ .byebug_history
19
+
20
+ ## Specific to RubyMotion:
21
+ .dat*
22
+ .repl_history
23
+ build/
24
+ *.bridgesupport
25
+ build-iPhoneOS/
26
+ build-iPhoneSimulator/
27
+
28
+ ## Specific to RubyMotion (use of CocoaPods):
29
+ #
30
+ # We recommend against adding the Pods directory to your .gitignore. However
31
+ # you should judge for yourself, the pros and cons are mentioned at:
32
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
+ #
34
+ # vendor/Pods/
35
+
36
+ ## Documentation cache and generated files:
37
+ /.yardoc/
38
+ /_yardoc/
39
+ /doc/
40
+ /rdoc/
41
+
42
+ ## Environment normalization:
43
+ /.bundle/
44
+ /vendor/bundle
45
+ /lib/bundler/man/
46
+
47
+ # for a library or gem, you might want to ignore these files since the code is
48
+ # intended to run in multiple environments; otherwise, check them in:
49
+ Gemfile.lock
50
+ # .ruby-version
51
+ # .ruby-gemset
52
+
53
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
+ .rvmrc
55
+
56
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
57
+ # .rubocop-https?--*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.1
7
+ before_install: gem install bundler -v 1.17.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cem.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 coezbek
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ # Cem = Christopher's Ruby Gem of Common Helpers
2
+
3
+ This gem is an assorted collection of common helpers that I use in a lot of projects.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'cem'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install cem
20
+
21
+ ## Usage
22
+
23
+ require 'cem'
24
+
25
+ Key things includes:
26
+
27
+ ### crequire
28
+
29
+ crequire is a replacement for require for situations where bundler would be overkill. It solves the frustration of having to manually install gems to run a particular ruby script which requires other gems.
30
+
31
+ For example when you want to require 'qml' from the 'ruby-qml' gem, use the following line:
32
+
33
+ ```
34
+ crequire 'qml', 'ruby-qml'
35
+ ```
36
+
37
+ This will attempt to require qml and if it can't be found in the local RubyGems, then it will try to `gem install ruby-qml`
38
+
39
+ ### Array::with_progress
40
+
41
+ Monkey patches array to return an enumeration which prints progress while lazy evaluating the enumeration.
42
+
43
+ ```
44
+ require 'cem'
45
+ [a,b,c].with_progress.each { |x|
46
+ puts x
47
+ }
48
+ ```
49
+
50
+ Uses the [progress bar gem](https://github.com/paul/progress_bar).
51
+
52
+ ### Numeric monkey patch
53
+
54
+ Make all methods in Math module available as methods for numeric (e.g. 3.sqrt instead of Math.sqrt(3))
55
+
56
+ ### Advent of Code Puzzle Helpers
57
+
58
+ Defines integer based `Point2D`, `Seg2D` (line segment), `Dir2D` (relative direction in 2D), `Point3D`, `Grid` (2D array) to help with Advent of Code puzzles, by providing commonly needed methods such as manhattan distances, string to indices conversion, enumeration of adjacent cells, etc.
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
63
+
64
+ To install this gem onto your local machine, run `bundle exec rake install`.
65
+
66
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
67
+
68
+ ## Contributing
69
+
70
+ Bug reports and pull requests are welcome on GitHub at https://github.com/coezbek/cem.
71
+
72
+ ## License
73
+
74
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
75
+
76
+ ## What's with the name?
77
+
78
+ Cem is pronounced like `Jam` in Turkish, which is close enough to `Gem` and exactely short enough to type.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cem"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,43 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "cem/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cem"
8
+ spec.version = Cem::VERSION
9
+ spec.authors = ["Christopher Özbek"]
10
+ spec.email = ["christopher@oezbek.org"]
11
+
12
+ spec.summary = %q{Cem = Christopher's Common Helper Gem for Ruby.}
13
+ spec.description = %q{Christopher's Gem = My little helpers which I use in many projects.}
14
+ spec.homepage = "https://github.com/coezbek/cem"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/coezbek/cem"
23
+ spec.metadata["changelog_uri"] = "https://github.com/coezbek/cem/README.md"
24
+ else
25
+ raise "RubyGems 2.0 or newer is required to protect against " \
26
+ "public gem pushes."
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_development_dependency "bundler", "~> 1.17"
39
+ spec.add_development_dependency "rake", "~> 10.0"
40
+ spec.add_development_dependency "rspec", "~> 3.0"
41
+
42
+ spec.add_runtime_dependency 'flammarion', '~> 0.3'
43
+ end
@@ -0,0 +1,10 @@
1
+
2
+ require "cem/version"
3
+ require "cem/ccommon"
4
+ require "cem/cflame"
5
+ require "cem/cruzzles"
6
+
7
+ module Cem
8
+ class Error < StandardError; end
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,93 @@
1
+
2
+ #
3
+ # Require the given ruby file and if loading fails, try to install the gem of the same name or given parameter
4
+ #
5
+ def crequire(requireName, gem_name = nil)
6
+ require requireName
7
+ rescue LoadError
8
+ gem_name = requireName if gem_name.nil?
9
+ system("gem install #{gem_name}")
10
+ Gem.clear_paths
11
+ require requireName
12
+ end
13
+
14
+ class File
15
+
16
+ # Returns the first match of the given files
17
+ def self.exist_first?(*candidates)
18
+ candidates.each { |candidate|
19
+ if File.exist?(candidate)
20
+ return candidate
21
+ end
22
+ }
23
+ return nil
24
+ end
25
+ end
26
+
27
+
28
+
29
+ class Array
30
+ def with_progress(&block)
31
+
32
+ crequire 'progress_bar'
33
+
34
+ bar = ProgressBar.new(count)
35
+ if block
36
+ each{|obj| yield(obj).tap{bar.increment!}}
37
+ else
38
+ Enumerator.new{ |yielder|
39
+ self.each do |obj|
40
+ (yielder << obj).tap{bar.increment!}
41
+ end
42
+ }
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ # Returns true if this script is running on the Windows Sub-System for Linux, i.e. under Linux where the host is Windows.
49
+ #
50
+ # If true, interoperability with Windows is possible.
51
+ def wsl?
52
+ @@wsl ||= File.file?('/proc/version') && File.open('/proc/version', &:gets).downcase.include?("microsoft")
53
+ end
54
+
55
+
56
+ #
57
+ # If on wsl? then the given is converted from a Windows path to a Linux path (e.g. "C:\" becomes "/mnt/c/")
58
+ #
59
+ # Uses the wslpath command, passes mode to wslpath:
60
+ #
61
+ # -a force result to absolute path format
62
+ # -u translate from a Windows path to a WSL path (default)
63
+ # -w translate from a WSL path to a Windows path
64
+ # -m translate from a WSL path to a Windows path, with '/' instead of '\'
65
+ #
66
+ def wslpath(path, mode = "")
67
+ return path if !wsl?
68
+
69
+ return `wslpath #{mode} '#{path}'`.strip
70
+ end
71
+
72
+ def isHTML5Element(element)
73
+
74
+ # https://html.spec.whatwg.org/#elements-3
75
+
76
+ @valid_elements ||= %w(a abbr address area article aside audio b base bdi bdo blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd label legend li link main map mark math menu meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select slot small source span strong style sub summary sup svg table tbody td template textarea tfoot th thead time title tr track u ul var video wbr)
77
+
78
+ return @valid_elements.include?(element.to_s.downcase)
79
+
80
+ end
81
+
82
+ module CLog
83
+
84
+ def log
85
+ CLog.log
86
+ end
87
+
88
+ # Global, memoized, lazy initialized instance of a logger
89
+ def self.log
90
+ @log ||= Logger.new(STDOUT)
91
+ end
92
+ end
93
+
@@ -0,0 +1,11 @@
1
+
2
+ #
3
+ # Christopher Flammarion extensions
4
+ #
5
+
6
+ require 'flammarion'
7
+
8
+ require "cem/cflame/missing_html"
9
+ require "cem/cflame/progress"
10
+ require "cem/cflame/p"
11
+ require "cem/cflame/clickable_img"
@@ -0,0 +1,58 @@
1
+ #
2
+ # Clickable Image module mixin for Flammarion::Writeable
3
+ #
4
+ # Features:
5
+ # - clickable => will call the given block when user clicks image
6
+ # - update() => can be replaced/updated
7
+ # - src= => img src can be updated on the fly
8
+ #
9
+ module Flammarion::Writeable
10
+
11
+ attr_accessor :url, :width, :height, :alt, :title
12
+
13
+ class ClickableImage
14
+ def initialize(url, width, height, alt, title, name, owner, block)
15
+ @name = name
16
+ @owner = owner
17
+ @block = block
18
+ @url = url
19
+ @width = width
20
+ @height = height
21
+ @alt = alt
22
+ @title = title
23
+ end
24
+
25
+ def to_s
26
+ return @owner.callback_link(html) {
27
+ @block.call(self) if @block
28
+ }
29
+ end
30
+
31
+ def html
32
+ %|<img id="#{@name}" width=#{@width.to_s} max-height=#{@height.to_s} src="#{@url.to_s}" alt="#{@alt}" title="#{@title}"/>|
33
+ end
34
+
35
+ def update(url, width, height, alt, title)
36
+ @url = url
37
+ @width = width
38
+ @height = height
39
+ @alt = alt if alt
40
+ @title = title if title
41
+
42
+ @owner.js("$( '##{@name}' ).replaceWith('#{html}');")
43
+
44
+ end
45
+
46
+ def url=(value)
47
+ value = value.to_s
48
+ # puts "urlset on #{@name}"
49
+ @owner.js("$( '##{@name}' ).attr('src', '#{value}');")
50
+ end
51
+
52
+ end
53
+
54
+ def img_raw(url, width, height, alt, title, options = {}, &block)
55
+ return ClickableImage.new(url, width, height, alt, title, @engraving.make_id, self, block)
56
+ end
57
+
58
+ end
@@ -0,0 +1,33 @@
1
+
2
+ # Mixin for Flammarion::Writeable which provides short-cuts for common html elements
3
+ #
4
+ # - The method name will become the html element / tag.
5
+ # - Any un-named parameter will become the value for the element. Several parameters will create several elements of the given tag.
6
+ # - Any named parameter will become an attribute to all created elements.
7
+ #
8
+ # Parameters will not be escaped (raw)!
9
+ #
10
+ # Some examples:
11
+ # f.p => f.puts "<p/>", raw: true
12
+ # f.h1 "Title" => f.puts "<h1>Title</h2>", raw: true
13
+ # f.img src: "url", width: 32 => f.puts '<img src="url" width="32"/>', raw: true
14
+ # f.p "Hello", "World" => f.puts "<p>Hello</p><p>World</p>", raw: true
15
+ #
16
+ module Flammarion::Writeable
17
+
18
+ def method_missing(m, *args, &block)
19
+
20
+ if !isHTML5Element(m)
21
+ puts "Warning: #{m} is not a valid HTML 5 Tag"
22
+ end
23
+
24
+ attribs = args.last.is_a?(Hash) ? args.pop.map { |key, value| %| #{key.to_s}="#{value}"| }.join : ""
25
+
26
+ if args.size == 0
27
+ puts "<#{m} #{attribs}/>", raw: true
28
+ else
29
+ puts args.map { |value| "<#{m} #{attribs}>#{value}</#{m}>" }.join, raw: true
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,37 @@
1
+
2
+ #
3
+ # Paragraph which can be updated easily as a mixin for Flammarion
4
+ #
5
+ module Flammarion::Writeable
6
+
7
+ class Paragraph
8
+
9
+ def initialize(text, name, owner)
10
+ @text = text
11
+ @name = name
12
+ @owner = owner
13
+ end
14
+
15
+ def html
16
+ %|<p id="#{@name}">#{@text}</p>|
17
+ end
18
+
19
+ def to_s
20
+ html
21
+ end
22
+
23
+ def text=(newText)
24
+ return if newText == @text
25
+ @text = newText
26
+
27
+ @owner.js("$( '##{@name}' ).replaceWith('#{html}');")
28
+ end
29
+ end
30
+
31
+ def p(text)
32
+ object = Paragraph.new(text, @engraving.make_id, self)
33
+ puts object, raw:true
34
+ object
35
+ end
36
+
37
+ end
@@ -0,0 +1,48 @@
1
+
2
+ #
3
+ # A simple CSS-based progress bar for Flammarion.
4
+ #
5
+ # Use set() to set the progress as an integer in percent.
6
+ #
7
+ # progress = f.progress
8
+ # ... # do first half
9
+ # progress.set(50)
10
+ # ... # finish work
11
+ # progress.set(100) # => will set to 100%
12
+ #
13
+ # Or use each() with an array
14
+ #
15
+ # progress.each([1,2,3]) { |x| ... }
16
+ #
17
+ module Flammarion::Writeable
18
+
19
+ class Progress
20
+ def initialize(name, owner)
21
+ @name = name
22
+ @owner = owner
23
+ @owner.raw("<div id='progressBar' style='width: 100%; height: 22px; border: 1px solid #8f8f8f; background-color: #292929; border-radius: 2px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25) inset;'><div id='progressBarInner-#{@name}' style='height: 100%; color: #fff; text-align: center; line-height: 22px; width: 0px; background-color: #0099ff;'></div></div>")
24
+ end
25
+
26
+ def each(ary, &block)
27
+ set(0)
28
+ @max = ary.size
29
+ return if @max == 0
30
+
31
+ ary.each_with_index { |a, i|
32
+ yield a
33
+ set(i * 100 / @max)
34
+ }
35
+ ary
36
+ end
37
+
38
+ def set(value)
39
+ value = value.to_s
40
+ @owner.js("$( '#progressBarInner-#{@name}' ).width('#{value}%').html('#{value}%');")
41
+ end
42
+
43
+ end
44
+
45
+ def progress(name = nil)
46
+ return Progress.new(name || @engraving.make_id, self)
47
+ end
48
+ end
@@ -0,0 +1,516 @@
1
+
2
+ ##
3
+ # cruzzles Christopher's Ruby Puzzle Utilities
4
+ #
5
+ # Includes:
6
+ # - Point2D, Grid, Dirs(ections)2D
7
+ # - Some Numeric hackery
8
+ #
9
+
10
+ def min(a, b)
11
+ return a <= b ? a : b
12
+ end
13
+
14
+ class Numeric
15
+ # Make all methods in Math module available as methods for numeric (e.g. 3.sqrt instead of Math.sqrt(3))
16
+ Math.methods(false).each do |method|
17
+ define_method method do |*args|
18
+ Math.send(method, self, *args)
19
+ end
20
+ end
21
+ end
22
+
23
+ Point2D = Struct.new("Point2D", :x, :y) {
24
+ def to_s
25
+ "#{x},#{y}"
26
+ end
27
+
28
+ def flip
29
+ Point2D.new(-x,-y)
30
+ end
31
+
32
+ def +(other)
33
+ if other.is_a? Array
34
+ other.map { |o| self + o }
35
+ else
36
+ Point2D.new(x + other.x, y + other.y)
37
+ end
38
+ end
39
+
40
+ # Scalar multiplication
41
+ def *(other)
42
+ Point2D.new(x * other, y * other)
43
+ end
44
+
45
+ def manhattan(other)
46
+ return (x - other.x).abs + (y - other.y).abs
47
+ end
48
+
49
+ # returns the euclidean distance to the given Point2D
50
+ def dist(other)
51
+ return ((x - other.x) ** 2 + (y - other.y) ** 2).sqrt
52
+ end
53
+
54
+ def self.from_s(s)
55
+
56
+ case s.upcase
57
+ when 'E', 'RIGHT', 'R', '>'
58
+ Point2D.new(+1, 0)
59
+ when 'N', 'UP', 'TOP', 'T', 'U', '^'
60
+ Point2D.new( 0, -1)
61
+ when 'S', 'DOWN', 'BOTTOM', 'B', 'D', 'v', 'V'
62
+ Point2D.new( 0, +1)
63
+ when 'W', 'LEFT', 'L', '<'
64
+ Point2D.new(-1, 0)
65
+ else
66
+ raise s
67
+ end
68
+
69
+ end
70
+
71
+ def left
72
+ return self + Dir2D.LEFT
73
+ end
74
+
75
+ def left!
76
+ x += Dir2D.LEFT.x
77
+ y += Dir2D.LEFT.y
78
+ return self
79
+ end
80
+
81
+
82
+ def to_dir_bitmask
83
+
84
+ if x == 0 && y == -1
85
+ return 1
86
+ elsif x == 1 && y == 0
87
+ return 2
88
+ elsif x == 0 && y == 1
89
+ return 4
90
+ elsif x == -1 && y == 0
91
+ return 8
92
+ else
93
+ raise self.p
94
+ end
95
+ end
96
+
97
+ def self.from_dir_bitmask(v)
98
+
99
+ result = []
100
+ if v & 1 > 0
101
+ result << Point2D.new(0, -1)
102
+ end
103
+ if v & 2 > 0
104
+ result << Point2D.new(1, 0)
105
+ end
106
+ if v & 4 > 0
107
+ result << Point2D.new(0, 1)
108
+ end
109
+ if v & 8 > 0
110
+ result << Point2D.new(-1, 0)
111
+ end
112
+ return result
113
+ end
114
+
115
+ # def <=>(other)
116
+ # y == other.y ? x <=> other.x : y <=> other.y
117
+ # end
118
+
119
+ def ==(other)
120
+ y == other.y && x == other.x
121
+ end
122
+ }
123
+
124
+ Seg2D = Struct.new("Seg2D", :p1, :p2) {
125
+ def to_s
126
+ "#{p1},#{p2}"
127
+ end
128
+
129
+ def x_range
130
+ if p1.x < p2.x
131
+ return p1.x..p2.x
132
+ else
133
+ return p2.x..p1.x
134
+ end
135
+ end
136
+
137
+ def y_range
138
+ if p1.y < p2.y
139
+ return p1.y..p2.y
140
+ else
141
+ return p2.y..p1.y
142
+ end
143
+ end
144
+
145
+ }
146
+
147
+ Dirs2D = [Point2D.new(0, -1), Point2D.new(1, 0), Point2D.new(0, 1), Point2D.new(-1, 0)]
148
+
149
+ class Dir2D < Point2D
150
+
151
+ N = UP = Dir2D.new( 0, -1)
152
+ E = RIGHT = Dir2D.new( 1, 0)
153
+ S = DOWN = Dir2D.new( 0, 1)
154
+ W = LEFT = Dir2D.new(-1, 0)
155
+
156
+ #
157
+ # Dir2D * Range = array of Dir2D
158
+ # E * (1..3) = [E, E*2, E*3]
159
+ #
160
+ def *(other)
161
+ if other.is_a? Range
162
+ other.to_a.map { |b| self * b }
163
+ else
164
+ super
165
+ end
166
+ end
167
+
168
+ def self.vert(length=1)
169
+ return [N * (1..length), S * (1..length)].flatten
170
+ end
171
+
172
+ def self.hori(length=1)
173
+ return [E * (1..length), W * (1..length)].flatten
174
+ end
175
+
176
+ def self.x(length=1)
177
+ return vert(length) + hori(length)
178
+ end
179
+
180
+ end
181
+
182
+
183
+ class Grid
184
+
185
+ attr_reader :data
186
+
187
+ def initialize(x, y, default=0, &block)
188
+
189
+ @data = []
190
+
191
+ if block
192
+
193
+ case block.arity
194
+ when 0
195
+ y.times {
196
+ row = []
197
+ x.times {
198
+ row << block.call()
199
+ }
200
+ @data << row
201
+ }
202
+ when 2
203
+ y.times { |iy|
204
+ row = []
205
+ x.times { |ix|
206
+ row << block.call(iy, ix)
207
+ }
208
+ @data << row
209
+ }
210
+ else
211
+ raise
212
+ end
213
+
214
+ else # if no block given
215
+
216
+ y.times {
217
+ row = []
218
+ x.times {
219
+ row << default
220
+ }
221
+ @data << row
222
+ }
223
+ # @data = [[default] * x] * y
224
+ end
225
+ end
226
+
227
+ def initialize_copy(orig)
228
+ super
229
+
230
+ @data = Marshal.load(Marshal.dump(orig.data))
231
+ # Do custom initialization for self
232
+ end
233
+
234
+ def minmax(null=nil)
235
+ min = nil
236
+ max = nil
237
+
238
+ @data.each_with_index { |l, y|
239
+ l.each_with_index { |c, x|
240
+
241
+ if c != null
242
+ if min == nil || max == nil
243
+ min = max = Point2D.new(x,y)
244
+ end
245
+ min = Point2D.new([min.x, x].min, [min.y, y].min)
246
+ max = Point2D.new([max.x, x].max, [min.y, y].max)
247
+ end
248
+ }
249
+ }
250
+
251
+ return min, max
252
+ end
253
+
254
+ def [](y, x=nil)
255
+ if x == nil
256
+ # puts y.inspect
257
+ if y.respond_to?(:x) && y.respond_to?(:y)
258
+ return @data[y.y][y.x]
259
+ end
260
+ return @data[y]
261
+ end
262
+
263
+ if y.is_a? Range
264
+ return @data[y].map { |row| row[x] }
265
+ end
266
+ return @data[y][x]
267
+ end
268
+
269
+ #
270
+ # Array assignment to grid.
271
+ #
272
+ # grid[y,x] = a # Store a in row y and column x
273
+ # grid[point2d] = a # Use point2d.y and point2d.x to store a
274
+ #
275
+ def []=(y, x, assign=nil)
276
+ if assign == nil
277
+ if y.respond_to?(:x) && y.respond_to?(:y)
278
+ return @data[y.y][y.x] = x
279
+ end
280
+ return @data[y] = x
281
+ end
282
+ return @data[y][x] = assign
283
+ end
284
+
285
+ def to_a
286
+ return @data.flatten
287
+ end
288
+
289
+ #
290
+ # Returns the Point2Ds in the directions NSEW as an array (no diagonals, see adja_index for this)
291
+ #
292
+ # Will not return Point2Ds in case we are at the boundary of the Grid
293
+ #
294
+ def nsew_index(x,y=nil)
295
+
296
+ # puts "#{@data.size} x #{@data[0].size}"
297
+
298
+ if y == nil
299
+ if x.respond_to?(:x) && x.respond_to?(:y)
300
+ y = x.y
301
+ x = x.x
302
+ else
303
+ raise "Need Point2D with :x and :y or two parameters"
304
+ end
305
+ end
306
+
307
+ result = []
308
+ if x > 0
309
+ result << Point2D.new(x-1,y)
310
+ end
311
+ if y > 0
312
+ result << Point2D.new(x,y-1)
313
+ end
314
+ if y + 1 < @data.size - 1
315
+ result << Point2D.new(x, y+1)
316
+ end
317
+ if x + 1 < @data[0].size - 1
318
+ result << Point2D.new(x+1,y)
319
+ end
320
+ return result
321
+ end
322
+
323
+ #
324
+ # Will remove all invalid indices from the array
325
+ #
326
+ def select_valid(a)
327
+ a.select { |p|
328
+ (p.x >= 0) && (p.y >= 0) && (p.y <= @data.size-1) && (p.x <= @data[0].size-1)
329
+ }
330
+ end
331
+
332
+ def adja_index(x,y=nil)
333
+ if y == nil
334
+ if x.respond_to?(:x) && x.respond_to?(:y)
335
+ y = x.y
336
+ x = x.x
337
+ else
338
+ raise "Need Point2D with :x and :y or two parameters"
339
+ end
340
+ end
341
+
342
+ result = []
343
+ if x > 0
344
+ if y > 0
345
+ result << Point2D.new(x-1,y-1)
346
+ end
347
+ result << Point2D.new(x-1,y)
348
+ if y + 1 < @data.size - 1
349
+ result << Point2D.new(x-1, y+1)
350
+ end
351
+ end
352
+ if y > 0
353
+ result << Point2D.new(x,y-1)
354
+ end
355
+
356
+ # result << Point2D.new(x,y)
357
+
358
+ if y + 1 < @data.size - 1
359
+ result << Point2D.new(x, y+1)
360
+ end
361
+ if x + 1 < @data[0].size - 1
362
+ if y > 0
363
+ result << Point2D.new(x+1,y-1)
364
+ end
365
+ result << Point2D.new(x+1,y)
366
+ if y + 1 < @data.size - 1
367
+ result << Point2D.new(x+1, y+1)
368
+ end
369
+ end
370
+ return result
371
+ end
372
+
373
+ def to_s
374
+ printBoard()
375
+ end
376
+
377
+
378
+ #
379
+ # returns a copy of the underlying 2D array, with linking to the content (rather than dup the content also)
380
+ #
381
+ def to_a
382
+ @data.map { |row| row.dup }
383
+ end
384
+
385
+ def data
386
+ @data
387
+ end
388
+
389
+ #
390
+ # Maps each cell of the grid by the given block.
391
+ #
392
+ # If block has...
393
+ # - one parameter, passes the cell value
394
+ # - two parameters, passes y and x
395
+ # - three parameteres, passes y, x and the cell value
396
+ #
397
+ def map_a(&block)
398
+
399
+ case block.arity
400
+ when 1
401
+ @data.map { |row| row.map { |value| block.call(value) } }
402
+ when 2
403
+ @data.each_with_index.map { |row, y| row.each_index.map { |x| block.call(y,x) } }
404
+ when 3
405
+ @data.each_with_index.map { |row, y| row.each_with_index.map { |cell, x| block.call(y,x,cell) } }
406
+ else
407
+ raise
408
+ end
409
+
410
+ end
411
+
412
+ def each(&block)
413
+ @data.each { |row|
414
+ row.each { |cell|
415
+ block.call(cell)
416
+ }
417
+ }
418
+ end
419
+
420
+ def each_with_index(&block)
421
+ case block.arity
422
+ when 2
423
+ @data.each_with_index { |l, y|
424
+ l.each_with_index { |c, x|
425
+ block.call(c, Point2D.new(x,y))
426
+ }
427
+ }
428
+
429
+ when 3
430
+ @data.each_with_index { |l, y|
431
+ l.each_with_index { |c, x|
432
+ block.call(c, y, x)
433
+ }
434
+ }
435
+ else
436
+ raise
437
+ end
438
+ end
439
+
440
+ def sum(&block)
441
+ sum = 0
442
+ each { |cell|
443
+ sum += block.call(cell)
444
+ }
445
+ return sum
446
+ end
447
+
448
+ def printBoard(overlay = nil)
449
+
450
+ result = ""
451
+ @data.each_with_index { |l, y|
452
+ l.each_with_index { |c, x|
453
+
454
+ pos = Point2D.new(x,y)
455
+
456
+ if overlay && overlay.has_key?(pos)
457
+ result += overlay[pos]
458
+ else
459
+ result += c.to_s
460
+ end
461
+ }
462
+
463
+ result += "\r\n"
464
+ }
465
+ return result
466
+ end
467
+
468
+ end
469
+
470
+ Point3D = Struct.new("Point3D", :x, :y, :z) {
471
+
472
+ def manhattan(other=nil)
473
+ if other != nil
474
+ return (self - other).manhattan
475
+ end
476
+ return x.abs + y.abs + z.abs
477
+ end
478
+
479
+ def -(other) Point3D.new(x - other.x, y - other.y, z - other.z) end
480
+
481
+ def +(other) Point3D.new(x + other.x, y + other.y, z + other.z) end
482
+
483
+ def to_s
484
+ "#{x}, #{y}, #{z}"
485
+ end
486
+
487
+ def []=(i, v)
488
+ case i
489
+ when 0
490
+ self.x = v
491
+ when 1
492
+ self.y = v
493
+ when 2
494
+ self.z = v
495
+ else
496
+ raise
497
+ end
498
+ end
499
+
500
+ def [](i)
501
+ case i
502
+ when 0
503
+ x
504
+ when 1
505
+ y
506
+ when 2
507
+ z
508
+ else
509
+ raise
510
+ end
511
+ end
512
+ }
513
+
514
+
515
+
516
+
@@ -0,0 +1,3 @@
1
+ module Cem
2
+ VERSION = "0.1.2"
3
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cem
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Christopher Özbek
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: flammarion
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ description: Christopher's Gem = My little helpers which I use in many projects.
70
+ email:
71
+ - christopher@oezbek.org
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - cem.gemspec
86
+ - lib/cem.rb
87
+ - lib/cem/ccommon.rb
88
+ - lib/cem/cflame.rb
89
+ - lib/cem/cflame/clickable_img.rb
90
+ - lib/cem/cflame/missing_html.rb
91
+ - lib/cem/cflame/p.rb
92
+ - lib/cem/cflame/progress.rb
93
+ - lib/cem/cruzzles.rb
94
+ - lib/cem/version.rb
95
+ homepage: https://github.com/coezbek/cem
96
+ licenses:
97
+ - MIT
98
+ metadata:
99
+ allowed_push_host: https://rubygems.org
100
+ homepage_uri: https://github.com/coezbek/cem
101
+ source_code_uri: https://github.com/coezbek/cem
102
+ changelog_uri: https://github.com/coezbek/cem/README.md
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.7.6
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Cem = Christopher's Common Helper Gem for Ruby.
123
+ test_files: []