Rosetta Examples development

It’s wonderful @monkstone!

This grid idea is also present in shoebot :slight_smile:, very nice. One can have something similar in Python mode doing this:

def grid(cols, rows, colSize=1, rowSize=1):
    """
    Returns an iterator that contains coordinate tuples.
    As seen in Shoebot & Nodebox (sans 'shuffled')
    A common way to use is:
    #    for x, y in grid(10, 10, 12, 12):
    #        rect(x, y, 10, 10)
    """
    rowRange = range(int(rows))
    colRange = range(int(cols))
    for y in rowRange:
        for x in colRange:
            yield (x * colSize, y * rowSize)
2 Likes

@villares The idea was first implemented by Jeremy Askenas in ruby-processing, and the inspiration was Nodebox :-

# A nice method to run a given block for a grid.
    # Lifted from action_coding/Nodebox.
    def grid(cols, rows, col_size = 1, row_size = 1)
      (0...cols * rows).map do |i|
        x = col_size * (i % cols)
        y = row_size * i.div(cols)
        yield x, y
      end
    end

For JRubyArt, I have taken it a bit further and implemented the ruby in java:-

  /**
  * Provides JRubyArt grid method as a ruby module method behaves like:-
  * def grid(dx, dy, sx = 1, sy = 1)
  *   (0...dx).step(sx) do |x|
  *     (0...dy).step(sy) do |y|
  *       yield(x, y)
  *     end
  *   end
  * end
  * @param context ThreadContext
  * @param recv IRubyObject
  * @param args array of args should be Fixnum
  * @param block { |x, y| `do something` }
  * @return nil
  */
  @JRubyMethod(name = "grid", rest = true, module = true)
  public static IRubyObject createGrid(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
    Ruby ruby = context.runtime;
    int row = (int) args[0].toJava(Integer.class);
    int column =  (int) args[1].toJava(Integer.class);
    int rowStep = 1;
    int colStep = 1;
    if (args.length == 4){
      rowStep = (int) args[2].toJava(Integer.class);
      colStep = (int) args[3].toJava(Integer.class);
    }
    if (block.isGiven()) {
      int tempRow = row / rowStep;
      int tempColumn = column /colStep;
      for (int z = 0; z < (tempRow * tempColumn); z++){
        int y = z % tempColumn;
        int x = z / tempColumn;
        block.yieldSpecific(context, ruby.newFixnum(x * rowStep), ruby.newFixnum(y * colStep));
      }
    }
    return context.nil;
  }

I guess it might be possible to do similar with jython and processing.py.

3 Likes

How interesting! Why the Java implementation? Optimal performance?

Something I didn’t like (understand?) in the shoebot & nodebox implementations was the shuffled mode. It shuffled rows an cols axis only and then iterated… strange.

I’m not 100% sure on performance front (it should be more performant as we are supplying java Integer rather than ruby Fixnum), but the main advantage is that once the classes are loaded it becomes part of ruby, and available in any class (and not dependent on the loading of a module). I have taken the idea slightly further and implemented a mesh_grid method for x. y. z.

1 Like

@monkstone – I don’t think I had actually gotten to JRubyArt categories on the wiki yet – another wiki editor may have changed them?

A question for you: on the wiki do you prefer JRubyArt listed on pages as its own language, or under Ruby, or under Processing?

  • Basic
  • JRubyArt
  • Processing
  • Ruby

or

  • Basic
  • Processing
  • Ruby
    • JRubyArt

or

  • Basic
  • Processing
    • JRubyArt
  • Ruby

Regardless it will have its own language category page.

@jeremydouglass It was simple change on the JRubyArt description page, perhaps you forgot. I believe it is best to keep JRubyArt under ruby on the Wiki, like Shoes where I follow the ruby convention of naming the gem, although confusingly there are two implementations of Shoes.

1 Like

Ah, yes, I recall now – there was something odd where it was a category of itself.

http://rosettacode.org/wiki/Category:JRubyArt

Good to know about it being organized as a library / gem on task pages:

  • Ruby
    • JRubyArt

I think that JRubyArt page still needs some template boilerplate added to its category page in order to get indexing etc – otherwise it isn’t being exposed to site search, which is too bad. I’ll look at that sometimes soon.

Hi @villares Something went wrong here.

Oh! Sorry for that @noel!

I had made a renamed fork, but then I removed it because now Jeremy’s repo has a place for Python mode examples :slight_smile:

"""
Dragon Curve https://www.rosettacode.org/wiki/Dragon_curve#Processing
 2020-02 Noel
 2020-03 Alexandre Villares (Python Mode)
Task: Create and display a dragon curve fractal.
https://en.wikipedia.org/wiki/Dragon_curve
(You may either display the curve directly or write it to an image file.)
For some brief notes the algorithms used and how they might suit various languages,
see the Rosetta Code task page.
"""

l = 3
ints = 13

def setup():
  size(700, 600)
  background(0, 0, 255)
  translate(150, 100)
  stroke(255)
  turn_left(l, ints)
  turn_right(l, ints)

def turn_right(l, ints):
    if ints == 0:
        line(0, 0, 0, -l)
        translate(0, -l)
    else:
        turn_left(l, ints - 1)
        rotate(radians(90))
        turn_right(l, ints - 1)
  
def turn_left(l, ints):
    if ints == 0:
        line(0, 0, 0, -l)
        translate(0, -l)
    else:
        turn_left(l, ints - 1)
        rotate(radians(-90))
        turn_right(l, ints - 1)
1 Like

I’ve posted the 15 Puzzle game task here.
Although the task only asks for the 4by4 set, it supports, from 3by3 to 5by5 and all the way up at any window size. It’s also on openProcessing here

.
@monkstone JRubyArt ? @villares Python ?

3 Likes

@noel I suppose there is no need to congratulate winner as it is obvious when you succeed, here is my JRubyArt version, I’ve stolen look and feel from your version:-

DIM = 100
NUMBER_CELLS = 16

attr_reader :tile, :bcolor, :dark, :light, :tcolor, :space, :blank
attr_reader :list, :inside, :target

def settings
  size(400, 400)
end

def setup
  sketch_title 'Fifteen Tile Puzzle'
  positions = []
  @list = []
  grid(width, height, DIM, DIM) do |y, x|
    positions << Vect.new(x, y)
  end
  positions.shuffle!
  positions.each_with_index do |pos, idx|
    list << Tile.new(idx.succ % 16, pos)
  end
  @tcolor = color(255, 175, 0)
  @bcolor = color(235, 231, 178)
  @dark = color(206, 141, 0)
  @light = color(255, 214, 126)
  text_size(DIM / 2.7)
  text_align(CENTER)
end

def draw
  list.each(&:draw)
end

def mouse_clicked
  update
  source = inside.pos
  if (source.x - target.pos.x).abs == DIM && source.y == target.pos.y
    inside.position(target.pos)
    target.position(source)
  elsif (source.y - target.pos.y).abs == DIM && source.x == target.pos.x
    inside.position(target.pos)
    target.position(source)
  end
end

def update
  @inside = list.find { |tile| tile.include?(Vect.new(mouse_x, mouse_y)) }
  @target = list.find { |tile| tile.number.zero? }
end

# Lightweight container for position
Vect = Struct.new(:x, :y)
# Our Tile Boundary Class
class Boundary
  attr_reader :low, :high

  def initialize(low, high)
    @low = low
    @high = high
  end

  def include?(val)
    return false unless (low.x...high.x).cover? val.x

    return false unless (low.y...high.y).cover? val.y

    true
  end
end

# Holds Tile logic and draw (Processing::Proxy give access to Sketch methods)
class Tile
  include Processing::Proxy
  attr_reader :boundary, :number, :pos

  def initialize(piece_nr, pos)
    @number = piece_nr
    @pos = pos
    @boundary = Boundary.new(pos, Vect.new(pos.x + DIM, pos.y + DIM))
  end

  def position(pos)
    @pos = pos
  end

  def draw_empty
    fill(bcolor)
    rect(pos.x + 1, pos.y + 1, DIM - 1, DIM - 1)
  end

  def draw_tile
    rect(pos.x + 1, pos.y + 1, DIM - 1, DIM - 1)
    fill(0) # Black text shadow
    text(number.to_s, pos.x + DIM / 2 + 1, pos.y + DIM / 2 + text_ascent / 2)
    fill(255)
    text(number.to_s, pos.x + DIM / 2, pos.y + DIM / 2 + text_ascent / 2)
    stroke(dark)
    line(pos.x + DIM - 1, pos.y + 1, pos.x + DIM - 1, pos.y + DIM - 2) # Right side shadow
    line(pos.x + 2, pos.y + DIM, pos.x + DIM - 1, pos.y + DIM - 2) # Bottom side shadow
    stroke(light)
    line(pos.x + 2, pos.y - 1, pos.x + 2, pos.y + DIM - 1) # Left bright
    line(pos.x + 2, pos.y + 1, pos.x + DIM - 1, pos.y + 1) # Upper bright
  end

  def include?(vec)
    boundary.include?(vec)
  end

  def draw
    no_stroke
    return draw_empty if number.zero?

    fill(tcolor)
    draw_tile
  end
end

2 Likes

I have posted a new improved version at Rosetta Code, original was bit flaky this version is lot more elegant too.

2 Likes

When packaging the above dragon curve sketch up in an example, like

dragon_curve/dragon_curve.rb

Would cf3 normally be included as an asset in the same directory?

It is quite usual with ruby not to include gem assets, where they are install-able from rubygems. To cope with version numbers etc most people would include a Gemfile and use Bundler to install the gems locally. It is just the ruby way of working, once you have the gems installed locally on your machine, they need not be installed again. CF3 is unusual in being so small, it could easily be installed as local library rather than as gem but I don’t with my examples. My math_demo gem is a example of how you could distribute a sketch as install-able application where providing you’ve got jruby installed you could simply:-

jgem install math_demo

then all the assets you require to run the sketch including propane (a simpler alternative to JRubyArt) get installed for you. After which you have app as installed gem and you can run it on commandline with:-

math_demo
1 Like

Wow! This is very cool!

Definitely need to add 15 Puzzle Game into the next release…

1 Like

And a Python mode version of @noel’s work should join it :wink:

# 15 Puzzle Game based on Noel's https://www.openprocessing.org/sketch/863055/

# Set the number of cells of the board here 9, 16, 25 etc
num_grid_cells = 16
piece_color = color(255, 175, 0)
background_color = color(235, 231, 178)
piece_shadow_dark = color(206, 141, 0)
piece_shadow_light = color(255, 214, 126)

def setup():
    global piece, piece_number, row_length, piece_side_length
    size(400, 400)  # Window size width and height must be egual
    background(200, 50, 0)
    row_length = int(sqrt(num_grid_cells))
    piece_side_length = width / row_length
    textSize(piece_side_length / 2.7)
    textAlign(CENTER)
    # Setting the x and y values for each cell on grid
    xy_val = []
    t = 0
    for i in range(0, num_grid_cells, row_length):
        for j in range(row_length):
            xy_val.append((j * piece_side_length,
                           t * piece_side_length))
        t += 1
    piece = []  # Puzzle piece objects
    placed = [False] * num_grid_cells  # to help placing the pieces randomly
    piece_number = 0
    # Placing pieces randomly in grid
    while (piece_number < num_grid_cells):
        p = int(random(0, num_grid_cells))
        # Once placed will be set to True to avoid adding again at this location
        if not placed[p]:
            # Creating the piece objects list
            piece.append(PuzzlePiece(piece_number, xy_val[p][0], xy_val[p][1]))
            placed[p] = True
            piece[piece_number].design()  # Draw newly create piece object
            piece_number += 1

def draw():
    # Search all piece object indexes and verify which one is mouse pressed
    for i in range(num_grid_cells):
        if (mousePressed and
                piece[i].x <= mouseX <= piece[i].x + piece_side_length and
                piece[i].y <= mouseY <= piece[i].y + piece_side_length and
                piece[i].piece_number != 15):
            if (pieceMove(piece[num_grid_cells - 1].x, piece[num_grid_cells - 1].y, piece[i].x, piece[i].y)):
                # Remember x and y value of final piece index (white piece)
                temp_x = int(piece[num_grid_cells - 1].x)
                temp_y = int(piece[num_grid_cells - 1].y)
                # Store clicked x and y value in final index of piece list
                piece[num_grid_cells - 1].set_pos(piece[i].x, piece[i].y)
                # Store temp x and y value (the last/previous final index
                # values) in current clicked piece
                piece[i].set_pos(temp_x, temp_y)
                # draw the final index piece index (only final piece index is
                # painted white)
                piece[num_grid_cells - 1].design()
                piece[i].design()  # Draw a numbered piece of current index

def pieceMove(final_index_piece_x, final_index_piece_y, current_index_x, current_index_y):
    # If both x's from clicked and white piece have same value meaning in same horizontal column
    # AND current clicked y value is equal to white piece y value - piece side lenght OR
    # current clicked y value + piece side lenght is egual to white piece y
    if (current_index_x == final_index_piece_x and (current_index_y == final_index_piece_y - piece_side_length or
                                                    (current_index_y == final_index_piece_y + piece_side_length))):
        return True
    # If both y's from clicked and white piece have same value meaning in same vertical column AND current clicked x value
    # is equal to white piece x value - piece side lenght OR current clicked x value + piece side lenght is
    # egual to white piece x
    elif (current_index_y == final_index_piece_y and (current_index_x == final_index_piece_x - piece_side_length or
                                                      (current_index_x == final_index_piece_x + piece_side_length))):
        return True
    else:
        return False

class PuzzlePiece:

    def __init__(self, pn, xp, yp):
        self.piece_number = pn
        self.x = xp
        self.y = yp

    def set_pos(self, xp, yp):
        self.x = xp
        self.y = yp

    def design(self):
        noStroke()
        fill(piece_color)
        if (self.piece_number == num_grid_cells - 1):
            fill(background_color)
        rect(self.x + 1, self.y + 1,
             piece_side_length - 1, piece_side_length - 1)
        if (self.piece_number != num_grid_cells - 1):
            fill(0)  # Black text shadow
            text(self.piece_number + 1, self.x + piece_side_length / 2 + 2,
                 self.y + piece_side_length / 2 + textAscent() / 2)
            fill(255)
            text(self.piece_number + 1, self.x + piece_side_length / 2,
                 self.y + piece_side_length / 2 + textAscent() / 2)
            stroke(piece_shadow_dark)
            line(self.x + piece_side_length - 1, self.y + 1, self.x +
                 piece_side_length - 1, self.y + piece_side_length - 1)  # Right side shadow
            line(self.x + 2, self.y + piece_side_length, self.x +
                 piece_side_length - 1, self.y + piece_side_length)  # Bottom side shadow
            stroke(piece_shadow_light)
            # Left bright
            line(self.x + 2, self.y - 1, self.x + 2,
                 self.y + piece_side_length)
            # Upper bright
            line(self.x + 2, self.y + 1, self.x +
                 piece_side_length - 1, self.y + 1)

2 Likes

This is a code for the Plasma effect task. It runs quite fluent around frameRate 60, but when I run it on OpenProcessing it slows ugly down. And it is not the palette build, because when I move it to setup() there is no speed increase. So I didn’t post it on Rosetta yet, because I would like to give a link to run it on OpenProcessing.
Maybe someone can point ou the reason.

/**
 Plasmas with Palette Looping
 https://lodev.org/cgtutor/plasma.html#Plasmas_with_Palette_Looping_
 */

int pal[] = new int[128];
int[] buffer;
float r = 42, g = 84, b = 126;
boolean rd, gd, bd;

void setup() {
  size(600, 600);
  frameRate(25);
  buffer = new int[width*height];
  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
      buffer[x+y*width] = int(((128+(128*sin(x/32.0)))
        +(128+(128*cos(y/32.0))) 
        +(128+(128*sin(sqrt((x*x+y*y))/32.0))))/4);
    }
  }
}

void draw() {
  if (r > 128) rd = true;
  if (!rd) r++;
  else r--;
  if (r < 0) rd = false;
  if (g > 128) gd = true;
  if (!gd) g++;
  else g--;
  if (r < 0) gd = false; 
  if (b > 128) bd = true;
  if (!bd) b++;
  else b--;
  if (b < 0){ bd = false;}
  float s_1, s_2;
  for (int i = 0; i < 128; i++) {
    s_1 = sin(i*PI/25);
    s_2 = sin(i*PI/50+PI/4);
    pal[i] = color(r+s_1*128, g+s_2*128, b+s_1*128);
  }
  loadPixels();
  for (int i = 0; i < buffer.length; i++) {                    
    pixels[i] =  pal[(buffer[i]+frameCount)&127];
  }
  updatePixels();
}
1 Like

I’ve spent some more time, but my solution was really to cheat.
On OpenProcessing I’ve put a different code drawing the pixels to a smaller PGraphics screen which is amplified then. Just as a show example I think it is forgivable.
Here is the link.

3 Likes

I’ve added the Bitmap/Floodfill task.
Here is the image I used.
Unfortunately, I couldn’t link it to OpenProcessing because the imports are not allowed.
I am trying to solve this here.
@glv @paulstgeorge I know you are always busy to respond to the topics which is, of course, more relevant. But maybe you could help here now and then. There still are a lot of tasks remaining to finish.

1 Like