Class: Board1010

Inherits:
Object
  • Object
show all
Defined in:
lib/games_and_rpg_paradise/gui/gosu/1010/board.rb

Overview

#

require ‘games_paradise/gui/gosu/1010/board.rb’

#

Constant Summary collapse

MAX_ROWS =

MAX_ROWS

10
MAX_COLS =

MAX_COLS

10
MAX_OPTIONS =

MAX_OPTIONS

3
GAME_FILE_NAME =

GAME_FILE_NAME

RUBY_ENGINE == "mruby" ? "./1010game.str" : "./1010game.dat"
DOT =
[[1]].freeze
HL =

Horizontal Lines

[[], [[1], [0]], [[2, 2], [0, 0]], [[3, 3, 3], [0, 0, 0]], [[4, 4, 4, 4], [0, 0, 0, 0]], [[5, 5, 5, 5, 5], [0, 0, 0, 0, 0]]].freeze
VL =

Vertical Lines

[[], [[1, 0]], [[1, 0], [1, 0]], [[1, 0], [1, 0], [1, 0]], [[1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]].freeze
SQ =

Squares

[
  [], [[1]],
  [[2, 2],
   [2, 2]],
  [[3, 3, 3],
   [3, 3, 3],
   [3, 3, 3]]
].freeze
BL =

Bottom Left Corners

[
  [], [[1]],
  [[2, 0],
   [2, 2]],
  [[3, 0, 0],
   [3, 0, 0],
   [3, 3, 3]]
].freeze
TL =

Top Left Corners

[
  [], [[1]],
  [[2, 2],
   [0, 2]],
  [[3, 3, 3],
   [0, 0, 3],
   [0, 0, 3]]
].freeze
BR =

Bottom Right Corners

[
  [], [[1]],
  [[0, 2],
   [2, 2]],
  [[0, 0, 3],
   [0, 0, 3],
   [3, 3, 3]]
].freeze
TR =

Top Right Corners

[
  [], [[1]],
  [[2, 2],
   [2, 0]],
  [[3, 3, 3],
   [3, 0, 0],
   [3, 0, 0]]
].freeze
ALL_TILES =
[
  DOT,

  HL[2],
  HL[3],
  HL[4],
  HL[5],

  VL[2],
  VL[3],
  VL[4],
  VL[5],

  SQ[2],
  SQ[3],

  BL[2],
  BL[3],

  TL[2],
  TL[3],

  BR[2],
  BR[3],

  TR[2],
  TR[3]
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rows = MAX_ROWS, columns = MAX_COLS) ⇒ Board1010

#

initialize

#


120
121
122
123
124
125
126
127
128
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 120

def initialize(
    rows    = MAX_ROWS,
    columns = MAX_COLS
  )
  @rows    = rows
  @cols    = columns
  @prev_options = @prev_score = @prev_arr = nil
  reset
end

Instance Attribute Details

#arrObject

Returns the value of attribute arr.



13
14
15
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 13

def arr
  @arr
end

#colsObject

Returns the value of attribute cols.



12
13
14
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 12

def cols
  @cols
end

#optionsObject

Returns the value of attribute options.



15
16
17
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 15

def options
  @options
end

#placed_posObject

Returns the value of attribute placed_pos.



17
18
19
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 17

def placed_pos
  @placed_pos
end

#prev_arrObject (readonly)

Returns the value of attribute prev_arr.



19
20
21
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 19

def prev_arr
  @prev_arr
end

#rowsObject

Returns the value of attribute rows.



11
12
13
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 11

def rows
  @rows
end

#scoreObject

Returns the value of attribute score.



14
15
16
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 14

def score
  @score
end

#valObject

Returns the value of attribute val.



16
17
18
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 16

def val
  @val
end

Instance Method Details

#andy_algo(_positions) ⇒ Object

andy_algo



544
545
546
547
548
549
550
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 544

def andy_algo(_positions)
  ## Suggested by Andy:
  #
  ## Find the position which results into minimum empty cells after placing the tile but before
  ## clearing the filled rows and columns
  -1
end

#backtrackObject

backtrack



358
359
360
361
362
363
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 358

def backtrack
  @placed_pos.each { |i, j, _k|
    _set_cell(i, j, 0)
  }
  @placed_pos = []
end

#best_starting_position(tile) ⇒ Object

best_starting_position



590
591
592
593
594
595
596
597
598
599
600
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 590

def best_starting_position(tile)
  positions = find_starting_pos(tile)
  # n = rand_fit(positions)
  # n = first_fit(positions)
  # n = last_fit(positions)
  # n = shanko_algo(positions)
  n = jc_algo(positions)
  # n = sam_algo(positions)
  # n = andy_algo(positions)
  positions[n || 0]
end

#cell=(triplet) ⇒ Object

cell=



283
284
285
286
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 283

def cell=(triplet)
  i, j, val = triplet
  _set_cell(i, j, val)
end

#cleanup(dry_run = false) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 324

def cleanup(dry_run=false)
  rows_to_clean = []
  @rows.times do |i|
    row_full = true
    @cols.times do |j|
      if _cell_empty?(i, j)
        row_full = false
        break
      end
    end
    rows_to_clean << i if row_full
  end

  cols_to_clean = []
  @cols.times do |j|
    col_full = true
    @rows.times do |i|
      if _cell_empty?(i, j)
        col_full = false
        break
      end
    end
    cols_to_clean << j if col_full
  end

  unless dry_run
    rows_to_clean.each {|i| row_cleanup(i) }
    cols_to_clean.each {|j| col_cleanup(j) }
  end

  [rows_to_clean, cols_to_clean]
end

#col_cleanup(j) ⇒ Object



318
319
320
321
322
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 318

def col_cleanup(j)
  @rows.times do |i|
    _set_cell(i, j, @val)
  end
end

#current_score?Boolean Also known as: current_score

#

current_score?

Feedback the current score.

#

Returns:

  • (Boolean)


266
267
268
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 266

def current_score?
  @score
end

#endedObject

ended



457
458
459
460
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 457

def ended
  e "\n--- Game ended: no place for remaining tiles"
  _over
end

#find_fitting_tileObject

find_fitting_tile

Improve this algorithm to find a better fit



373
374
375
376
377
378
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 373

def find_fitting_tile
  # e "*** Finding a fitting tile ***"
  tiny_tiles = [HL[2], VL[2], SQ[2], BL[2], TL[2], BR[2], TR[2], DOT]
  _i, _j, tile = pos_exists?(tiny_tiles)
  tile
end

#find_starting_pos(tile) ⇒ Object

find_starting_pos



488
489
490
491
492
493
494
495
496
497
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 488

def find_starting_pos(tile)
  positions = []
  @arr.each_with_index do |row, i|
    row.each_with_index do |_col, j|
      score = place(tile, i, j, true)
      positions << [i, j] if score > 0
    end
  end
  positions
end

#first_fit(_positions) ⇒ Object



574
575
576
577
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 574

def first_fit(_positions)
  # The best position is always the first position found
  0
end

#generate_tiles(n = Board1010::MAX_OPTIONS) ⇒ Object



380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 380

def generate_tiles(n=Board1010::MAX_OPTIONS)
  opt_tiles = []

  n.times do |_i|
    r = rand(ALL_TILES.size)
    tile = ALL_TILES[r]
    opt_tiles << tile
  end

  opt_tiles[0] = find_fitting_tile unless pos_exists?(opt_tiles)

  opt_tiles
end

#get_positionObject

#

get_position

#


302
303
304
305
306
307
308
309
310
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 302

def get_position
  print "Select Row, Col: "
  line = gets.chomp
  x, y = line.split(",")
  stop if x.to_i < 0 || y.to_i < 0
  i = x.to_i % Board1010::MAX_ROWS
  j = y.to_i % Board1010::MAX_COLS
  [i, j]
end

#get_selected_option(i, j) ⇒ Object



288
289
290
291
292
293
294
295
296
297
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 288

def get_selected_option(i, j)
  e "Select one option for position (#{i}, #{j}):"
  loop do
    print "option> "
    option = gets.chomp
    stop if option.to_s.casecmp("q").zero?
    return option.to_i if [1, 2, 3].include?(option.to_i)
    e "Invalid option #{option}. Should be one of [1,2,3]"
  end
end

#init(val = nil) ⇒ Object

init



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 236

def init(val = nil)
  case val.class
  when Array
    @arr   = val
    @score = _filled_cells
  when Numeric
    @val = val
    @arr = Array.new(@rows) { Array.new(@cols) { val } }
    @score = 0
    @options = []
  else
    if game = load_game
      @rows    = Board1010::MAX_ROWS
      @cols    = Board1010::MAX_COLS
      @val     = 0
      @arr     = game.arr
      @score   = game.score
      @options = game.options
    else
      @arr = Array.new(@rows) { Array.new(@cols) { @val } }
    end
  end
  start
end

#jc_algo(positions) ⇒ Object



552
553
554
555
556
557
558
559
560
561
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 552

def jc_algo(positions)
  # Suggested by JC:
  # The best position is one which has most neighbors occupied.
  # But treat the edge cells as if their neighbors are always occupied.
  neighbour_count = []
  positions.each_with_index do |pos, k|
    neighbour_count[k] = jc_neighbours(*pos)
  end
  neighbour_count.index(neighbour_count.max)
end

#jc_neighbours(i, j) ⇒ Object

jc_neighbours



517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 517

def jc_neighbours(i, j)
  count = 0

  count += 1 if i > 0 && j > 0 && (@arr[i - 1][j - 1]).nonzero?
  count += 1 if i > 0 && (@arr[i - 1][j]).nonzero?
  count += 1 if i > 0 && j < 9 && (@arr[i - 1][j + 1]).nonzero?

  count += 1 if i < 9 && j > 0 && (@arr[i + 1][j - 1]).nonzero?
  count += 1 if i < 9 && (@arr[i + 1][j]).nonzero?
  count += 1 if i < 9 && j < 9 && (@arr[i + 1][j + 1]).nonzero?

  count += 1 if j > 0 && (@arr[i][j - 1]).nonzero?
  count += 1 if j < 9 && (@arr[i][j + 1]).nonzero?

  count += 1 if i.zero? || (i == 9)
  count += 1 if j.zero? || (j == 9)

  count
end

#last_fit(_positions) ⇒ Object



579
580
581
582
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 579

def last_fit(_positions)
  # The best position is always the last position found
  -1
end

#load_gameObject

load_game



205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 205

def load_game
  game = nil
  if File.exist?(GAME_FILE_NAME)
    File.open(GAME_FILE_NAME, "rb") do |f|
      data = f.read
      game = if RUBY_ENGINE == "mruby"
               parse_load(data)
             else
               Marshal.load(data)
             end
    end
  end
  game
end

#marshal_dumpObject

#

marshal_dump

Save important stuff, such as the user’s score.

#


153
154
155
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 153

def marshal_dump
  [@score, @arr, @options]
end

#marshal_load(array) ⇒ Object

#

marshal_load

#


160
161
162
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 160

def marshal_load(array)
  @score, @arr, @options = array
end

#neighbours(i, j) ⇒ Object



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 499

def neighbours(i, j)
  count = 0

  count += 1 if i > 0 && j > 0 && (@arr[i - 1][j - 1]).nonzero?
  count += 1 if i > 0 && (@arr[i - 1][j]).nonzero?
  count += 1 if i > 0 && j < 9 && (@arr[i - 1][j + 1]).nonzero?

  count += 1 if i < 9 && j > 0 && (@arr[i + 1][j - 1]).nonzero?
  count += 1 if i < 9 && (@arr[i + 1][j]).nonzero?
  count += 1 if i < 9 && j < 9 && (@arr[i + 1][j + 1]).nonzero?

  count += 1 if j > 0 && (@arr[i][j - 1]).nonzero?
  count += 1 if j < 9 && (@arr[i][j + 1]).nonzero?

  count
end

#parse_load(data) ⇒ Object

#

parse_load

#


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 179

def parse_load(data)
  bGame = Struct.new(
    'Game', :rows, :cols, :score, :val, :options, :arr
  )
  game = bGame.new
  data.split(';').each do |element|
    key, val = element.split("=")
    case key
    when '@rows'
      game.rows = val.to_i
    when '@cols'
      game.cols = val.to_i
    when '@score'
      game.score = val.to_i
    when '@val'
      game.val = val.to_i
    when '@options'
      game.options = [[1], [1, 1, 1], [-1, -1, -1]]
    when '@arr'
      game.arr = Array.new(game.rows) { Array.new(game.cols) { @val } }
    end
  end
  game
end

#place(tile, i, j, dry_run = false) ⇒ Object

#

place

#


605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 605

def place(tile, i, j, dry_run=false)
  score = 0 # Calculate the score.
  @placed_pos = []
  tile.each_with_index do |cell, r|
    if r.zero?
      cell.each { |entry|
        break if entry > 0
        j -= 1
      }
      return 0 if j < 0
    end
    cell.each_with_index do |ele, c|
      if ele > 0 && _cell_occupied?(i + r, j + c)
        backtrack unless dry_run
        return 0
      end
      next unless ele > 0
      _set_cell(i + r, j + c, ele) unless dry_run
      @placed_pos << [i + r, j + c, ele]
      score += 1
    end
  end
  # Add it to the main scrore.
  @score += score unless dry_run
  score
end

#playObject

play



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 425

def play
  loop do
    all = (!@options.empty? ? @options : generate_tiles)
    @options = all
    until all.empty?
      show
      e "\n#{'=' * 20}\nCurrent score = #{current_score}\n#{'=' * 20}\n"
      show_tiles(all)
      ended unless pos_exists?(all)
      i, j = get_position
      opt = if all.size > 1
              get_selected_option(i, j)
            else
              1
            end
      tile = all[opt - 1]
      new_score = place(tile, i, j)
      if new_score < 1
        e " Cannot place the tile #{tile} at (#{i}, #{j})"
      else
        all.delete_at(opt - 1)
        cleanup
      end
    end
  end
end

#pos_exists?(tiles, debug = false) ⇒ Boolean

Returns:

  • (Boolean)


406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 406

def pos_exists?(tiles, debug=false)
  tiles.each do |tile|
    if debug
      e "Checking if tile #{tile} can be placed:"
      show
    end
    @arr.each_with_index do |row, i|
      row.each_with_index do |_col, j|
        score = (_cell_occupied?(i, j) ? 0 : place(tile, i, j, true))
        e "score = #{score} at (#{i},#{j})" if debug
        return [i, j, tile] if score > 0
      end
    end
  end
  e "...no can place" if debug
  false
end

#rand_fit(positions) ⇒ Object



584
585
586
587
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 584

def rand_fit(positions)
  # The best position is always a random position found
  rand(positions.size)
end

#resetObject

#

reset

#


133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 133

def reset
  STDOUT.sync = true
  # === @arr
  @arr     = nil
  # ======================================================================= #
  # === @score
  #
  # This variable keeps track of the user's current score. It will
  # start at score 0.
  # ======================================================================= #
  @score   = 0
  @val     = 0
  @options = []
end

#restore_arr(prev_arr) ⇒ Object

#

restore_arr

#


273
274
275
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 273

def restore_arr(prev_arr)
  @arr = Marshal.load(Marshal.dump(prev_arr))
end

#restore_options(prev_options) ⇒ Object

restore_options



278
279
280
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 278

def restore_options(prev_options)
  @options = Marshal.load(Marshal.dump(prev_options))
end

#row_cleanup(i) ⇒ Object



312
313
314
315
316
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 312

def row_cleanup(i)
  @cols.times do |j|
    _set_cell(i, j, @val)
  end
end

#sam_algo(_positions) ⇒ Object



537
538
539
540
541
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 537

def sam_algo(_positions)
  ## Suggested by Sam:
  ## Find the position which completely fills more rows and columns when the tile is placed
  -1
end

#saveObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 220

def save
  print "Do you want to save the game to play it later? [y/n] "
  opt = gets.chomp.downcase
  if opt == "y"
    File.open(GAME_FILE_NAME, "wb") do |f|
      if RUBY_ENGINE == "mruby"
        f.write(to_s)
      else
        f.write(Marshal.dump(self))
      end
    end
  end
  (opt == "y")
end

#shanko_algo(positions) ⇒ Object

shanko_algo



564
565
566
567
568
569
570
571
572
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 564

def shanko_algo(positions)
  # The best position is one which has most neighbors occupied.
  # In other words: least neighboring cells empty
  neighbour_count = []
  positions.each_with_index { |pos, k|
    neighbour_count[k] = neighbours(*pos)
  }
  neighbour_count.index(neighbour_count.max)
end

#showObject

show



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 469

def show
  e ''
  print "   "; @cols.times {|j| print " #{j}" }
  e
  @arr.each_with_index do |row, i|
    print " #{i}["
    row.each do |col|
      if col.zero?
        print " ."
      else
        print " X"
      end
    end
    e " ]"
  end
  e ''
end

#show_tile(tile) ⇒ Object

show_title



366
367
368
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 366

def show_tile(tile)
  p tile
end

#show_tiles(all) ⇒ Object

show_tiles



395
396
397
398
399
400
401
402
403
404
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 395

def show_tiles(all)
  n = 1
  all.each do |tile|
    next unless tile
    print " #{n}> "
    show_tile(tile)
    n += 1
  end
  n
end

#startObject



452
453
454
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 452

def start
  @start_time = Time.now
end

#stopObject



462
463
464
465
466
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 462

def stop
  e "\n--- Game stopped: to be played later"
  save
  _over
end

#to_sObject

#

to_s

#


167
168
169
170
171
172
173
174
# File 'lib/games_and_rpg_paradise/gui/gosu/1010/board.rb', line 167

def to_s
  opt_str = ''.dup
  @options.each { |opt|
    opt_str << opt.inject('') {|sum, c| sum + c.to_s }
    opt_str << ","
  }
  "@rows=#{@rows};@cols=#{@cols};@score=#{@score};@val=#{@val};@options=#{@options};@arr=#{@arr}"
end