From 7f464371021e8fca1c8069031460297e32ea3b1f Mon Sep 17 00:00:00 2001 From: jaysonesmith Date: Wed, 12 Jun 2019 23:27:15 -0700 Subject: [PATCH] Add Ruby --- README.md | 1 + templates/ruby/btree.go | 83 ++++++++++++ templates/ruby/eightbitadder.go | 55 ++++++++ templates/ruby/reflextest.go | 215 ++++++++++++++++++++++++++++++++ templates/templates.go | 10 ++ 5 files changed, 364 insertions(+) create mode 100644 templates/ruby/btree.go create mode 100644 templates/ruby/eightbitadder.go create mode 100644 templates/ruby/reflextest.go diff --git a/README.md b/README.md index c53c21f..822c222 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ $ super-hacker -b 16 - Java - Javascript - Rockstar +- Ruby ## Installation diff --git a/templates/ruby/btree.go b/templates/ruby/btree.go new file mode 100644 index 0000000..5703977 --- /dev/null +++ b/templates/ruby/btree.go @@ -0,0 +1,83 @@ +package ruby + +// https://github.com/seifertd/Ruby-BTree + +const Btree = ` +Copyright (c) 2010,2011 Douglas A. Seifert + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# :main: README.md +module Btree + + # :stopdoc: + LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR + PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR + # :startdoc: + + # Returns the version string for the library. + # + def self.version + @version ||= File.read(path('version.txt')).strip + end + + # Returns the library path for the module. If any arguments are given, + # they will be joined to the end of the libray path using + # File.join. + # + def self.libpath( *args, &block ) + rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten) + if block + begin + $LOAD_PATH.unshift LIBPATH + rv = block.call + ensure + $LOAD_PATH.shift + end + end + return rv + end + + # Returns the lpath for the module. If any arguments are given, + # they will be joined to the end of the path using + # File.join. + # + def self.path( *args, &block ) + rv = args.empty? ? PATH : ::File.join(PATH, args.flatten) + if block + begin + $LOAD_PATH.unshift PATH + rv = block.call + ensure + $LOAD_PATH.shift + end + end + return rv + end + + # Utility method used to require all files ending in .rb that lie in the + # directory below this file that has the same name as the filename passed + # in. Optionally, a specific _directory_ name can be passed in such that + # the _filename_ does not have to be equivalent to the directory. + # + def self.require_all_libs_relative_to( fname, dir = nil ) + dir ||= ::File.basename(fname, '.*') + search_me = ::File.expand_path( + ::File.join(::File.dirname(fname), dir, '**', '*.rb')) + + Dir.glob(search_me).sort.each {|rb| require rb} + end + + def self.create(degree) + raise "Degree of Btree must be >= 2" if degree < 2 + return Btree::Tree.new(degree) + end + +end # module Btree + +Btree.require_all_libs_relative_to(__FILE__) +` diff --git a/templates/ruby/eightbitadder.go b/templates/ruby/eightbitadder.go new file mode 100644 index 0000000..572fb43 --- /dev/null +++ b/templates/ruby/eightbitadder.go @@ -0,0 +1,55 @@ +package ruby + +// http://sandbox.mc.edu/~bennet/ruby/code/ctest3_rb.html + +const EightBitAdder = ` +# +# This is a one-bit adder. +# + +require "csim" +require "cgrp" +require "oba" + +NumberOut.shush + +# Blueprint for a the one-bit adder +bp = OBA.new + +# Two input senders, and the output device. +na = SwitchBank.new +nb = SwitchBank.new +disp = NumberOut.new(" Sum") + +# We're going to build an 8-bit adder +prev = nil +8.times do + # Create the one-bit adder and join the data inputs and outputs. + addr = bp.another + na.join(addr) + nb.join(addr) + addr.join(disp) + + # Chain the carry, if this isn't he first one. + if prev then + prev.join(addr) + end + + prev = addr +end + +# Overflow light. +prev.join(LED.new(" Oflow")) + +NumberOut.shush(false) + +30.times do + a = rand(256) + b = rand(256) + print a, " + ", b, ":\n" + Gate.activate + na.value = a + nb.value = b + Gate.deactivate +end +` diff --git a/templates/ruby/reflextest.go b/templates/ruby/reflextest.go new file mode 100644 index 0000000..182b651 --- /dev/null +++ b/templates/ruby/reflextest.go @@ -0,0 +1,215 @@ +package ruby + +// http://sandbox.mc.edu/~bennet/ruby/code/blink_rb.html + +const ReflexTest = ` +#!/usr/bin/ruby + +# Import the library. +require 'tk' + +# Parameters. +Width = 5 # Width of button grid. +Height = 5 # Height of button grid. +MinWait = 200 # Smallest button change wait (ms) +MaxWait = 1400 # Largest button change wait (ms) +InitWait = 800 # Initial button change wait (ms) +LossRate = 2000 # Frequency to take away points. + +# Set defaults. Some we keep in constants to use later. +BG = '#ccffcc' +TkOption.add('*background', BG) +TkOption.add('*activeBackground', '#ddffdd') +FG = '#006600' +TkOption.add('*foreground', FG) +TkOption.add('*activeForeground', FG) +TkOption.add('*troughColor', '#99dd99') + +# Root window. +root = TkRoot.new('background' => BG) { title 'Click Fast' } + +# Button from the panel +class PanelButton < TkButton +private + # Exchange colors on the button. + def cswap + for p in [['background', 'foreground'], + ['activebackground', 'activeforeground']] + c = cget(p[0]) + configure(p[0] => cget(p[1])) + configure(p[1] => c) + end + end +public + # Initialize the button within the widget sup, at position pos (zero-based) + # with the number num. When pressed, send the score (+ or -) to cmd. + # Scorekeeper is an object which implements an up and down methods to + # receive score changes. + def initialize(sup, pos, num, scorekeeper) + super(sup, 'text' => num.to_s, 'command' => proc { self.pushed }, + 'activeforeground' => '#990000', 'activebackground' => '#ffdddd') + grid('row' => pos / Width + 1, 'column' => pos % Width, 'sticky' => 'news') + @active = false + @scorekeeper = scorekeeper + end + attr_reader :active + + # Activate or deactivate the button. + def activate + if not @active + cswap + @active = true + end + end + def deactivate + if @active + cswap + @active = false + end + end + + # When pushed, send our number, or negative our number, to the scorekeeping + # command. + def pushed + n = self.cget('text').to_i + if @active + @scorekeeper.up(n) + else + @scorekeeper.down(n) + end + end +end + +# This class calls reduces the score at the indicated time rate. +class ScoreTimer + # This object will call scorekeeper.down(step) each rate ms. + def initialize(scorekeeper, rate = 500, step = 1) + @scorekeeper = scorekeeper + @rate = rate + @step = step + + Tk.after(rate, proc { self.change }) + end + + # Reduce the score periodically + def change + @scorekeeper.down(@step) + Tk.after(@rate, proc { self.change }) + end +end + +# This is a box displaying a count-up timer in minutes and seconds to tenths +# m:ss.d +class TimeCounter < TkLabel + # Initialize. Displays zero and starts the ticking event. + def initialize(root) + super(root, "text" => '0:00.0', 'anchor' => 'e') + @count = 0 + Tk.after(100, proc { self.change }) + end + + # One clock tick (tenths of a second). Increment the counter, then build + # the new display value. + def change + @count += 1 + self.configure('text' => + sprintf("%d:%02d.%d", + @count / 600, (@count / 10) % 60, @count % 10)) + Tk.after(100, proc { self.change }) + end +end + +# This is the main application GUI. +class App +private + # Set the score value. + def setscore(val) + color = if val < 0 then 'red' else FG end + @slab.configure('text' => val.to_s, 'foreground' => color) + end +public + # The wait attribute is the amount of time (ms) between button changes. + attr_writer :wait + + # Initialize it and have the applicate drawn in the root window. + def initialize(root) + # This is the label containing the score. Initially zero. + @slab = TkLabel.new(root) { + text "0" + anchor 'e' + grid('row' => 0, 'column' => 0, 'columnspan' => Width / 2, + 'sticky' => 'w') + } + + # This is the timer window at upper right. + TimeCounter.new(root). + grid('row' => 0, 'column' => Width/2, 'columnspan' => (Width+1)/2, + 'sticky' => 'e') + + # Create the buttons. First, make an array of numbers from 1 to the + # number of buttons, then create the buttons, each labelled with a + # number chosen at random from the list, so thare are no repeats. + nums = (1..Height*Width).to_a; + @buts= [ ] + for n in (0...Height*Width) + pos = rand(nums.length) + @buts.push(PanelButton.new(root, n, nums[pos], self)) + nums.delete_at(pos) + end + + # This creates the slider to adjust the speed of the game. The proc is + # called whenever the slider changes, and is sent the new setting. + scale = TkScale.new('command' => proc { |v| self.wait = v.to_i } ) { + orient "horizontal" # Which way the slider goes. + from MinWait # Value of smallest setting + to MaxWait # Value of largest setting + showvalue false # Don't show the numeric value of the setting. + grid('row' => Height + 1, 'column' => 1, 'columnspan' => Width - 2, + 'sticky' => 'news') + } + scale.set(InitWait) + + # Labels by the slider. + TkLabel.new { + text "Fast" + anchor "w" + grid("row" => Height + 1, 'column' => 0, 'sticky' => 'w') + } + TkLabel.new { + text "Slow" + anchor "e" + grid("row" => Height + 1, 'column' => Width-1, 'sticky' => 'e') + } + + @wait = InitWait + + # Decrement the score every LossRate period. + @timer = ScoreTimer.new(self, LossRate) + self.change + end + + # Actions to increase or decrease the score. + def up(delta) + setscore(@slab.cget('text').to_i + delta) + end + def down(delta) + setscore(@slab.cget('text').to_i - delta) + end + + # Change (or set, if none is yet set) the active button. It deactivates + # the button in @buts[0], It then chooses some other button at random, + # activates that and swaps it into position 0. + def change + @buts[0].deactivate + pos = rand(@buts.length - 1) + 1 + @buts[0], @buts[pos] = @buts[pos], @buts[0] + @buts[0].activate + + Tk.after(@wait, proc { self.change }) + end +end + +a = App.new(root) + +Tk.mainloop +` diff --git a/templates/templates.go b/templates/templates.go index aa95342..3a62fe5 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -12,6 +12,7 @@ import ( "github.com/briandowns/super-hacker/templates/javascript" "github.com/briandowns/super-hacker/templates/python" "github.com/briandowns/super-hacker/templates/rockstar" + "github.com/briandowns/super-hacker/templates/ruby" "github.com/briandowns/super-hacker/templates/scala" ) @@ -81,6 +82,13 @@ var rockstarTemplates = []string{ rockstar.NinetyNineBottles, } +// rubyTemplates holds all active Rockstar templates. +var rubyTemplates = []string{ + ruby.Btree, + ruby.EightBitAdder, + ruby.ReflexTest, +} + // Random selects a template from the template slice // at random and returns it to the caller. func Random(lang string) (string, error) { @@ -101,6 +109,8 @@ func Random(lang string) (string, error) { return javascriptTemplates[rand.Intn(len(javascriptTemplates))], nil case "rockstar": return rockstarTemplates[rand.Intn(len(rockstarTemplates))], nil + case "ruby": + return rubyTemplates[rand.Intn(len(rubyTemplates))], nil default: return "", errors.New("unsupported language") }