bloodymines 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/bloodymines ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+ # bloodymines -- the executable terminal script for the bloody_mines gem
3
+ require File.dirname(__FILE__) + "/../lib/minefield"
4
+ require "curses"
5
+ include Curses
6
+
7
+ module Curses
8
+ def self.program
9
+ main_screen = init_screen # init curses
10
+ noecho # don't show what you type
11
+ trap(0) { echo } # reactivate echo on exit
12
+ cbreak # turn off buffered input
13
+ curs_set(0) # hide cursor
14
+ main_screen.keypad = true # cursor keys are real events
15
+ yield main_screen
16
+ end
17
+ end
18
+
19
+ class Game
20
+ attr_accessor :cursor_x, :cursor_y
21
+ attr_reader :mines, :x_max, :y_max
22
+ def initialize(space = 1, x_offset = 1, y_offset = 1)
23
+ @space, @x_offset, @y_offset = space, x_offset, y_offset
24
+ @cursor_x, @cursor_y = 0, 0
25
+
26
+ puts "Welcome to bloody mines!"
27
+ print "Select your difficulty: [1]beginner, [2]intermediate, [3]expert => "
28
+ difficulty = gets.chomp.to_i
29
+ difficulty = case difficulty
30
+ when 2: :intermediate
31
+ when 3: :expert
32
+ else :beginner
33
+ end
34
+ @mines = MineField.new(:difficulty => difficulty)
35
+ @x_max = @mines.fields.size - 1
36
+ @y_max = @mines.fields[0].size - 1
37
+ end
38
+
39
+ def score
40
+ score = 0
41
+ @mines.fields.each do |rows|
42
+ rows.each do |cell|
43
+ score += cell if cell.kind_of?(Integer)
44
+ end
45
+ end
46
+ score
47
+ end
48
+
49
+ def draw_field
50
+ clear
51
+ 0.upto(@x_max) do |x|
52
+ 0.upto(@y_max) do |y|
53
+ # curses swaps x and y
54
+ setpos(y + @y_offset, x + @x_offset + spacing(x, @space))
55
+ field = case @mines.fields[x][y]
56
+ when false, :mine: "."
57
+ when :boom: "X"
58
+ else @mines.fields[x][y].to_s
59
+ end
60
+ addstr(field)
61
+ end
62
+ end
63
+ draw_cursor
64
+ setpos(0, 1)
65
+ addstr("Score: #{score}")
66
+ setpos(@y_max + 3, 1)
67
+ addstr("Use the arrow keys to move around.\n Hit Space to reveal a field. Q or ESC quits the game.")
68
+ refresh
69
+ end
70
+
71
+ def spacing(x, space)
72
+ x * space
73
+ end
74
+
75
+ def draw_cursor
76
+ x = @cursor_x + @x_offset + spacing(@cursor_x, @space)
77
+ y = @cursor_y + @y_offset
78
+ setpos(y, x - 1)
79
+ addstr("[")
80
+ setpos(y, x + 1)
81
+ addstr("]")
82
+ end
83
+
84
+ def move(params={})
85
+ case params[:direction]
86
+ when :up
87
+ @cursor_y -= 1 if @cursor_y > 0
88
+ when :down
89
+ @cursor_y += 1 if @cursor_y < @y_max
90
+ when :left
91
+ @cursor_x -= 1 if @cursor_x > 0
92
+ when :right
93
+ @cursor_x += 1 if @cursor_x < @x_max
94
+ end
95
+ end
96
+
97
+ def ended?
98
+ uncovered = 0
99
+ @mines.fields.each do |rows|
100
+ rows.each do |cell|
101
+ uncovered += 1 if cell == false
102
+ return true if cell == :boom
103
+ end
104
+ end
105
+ return false if uncovered > 0
106
+ true
107
+ end
108
+ end
109
+
110
+ # curses app
111
+ game = Game.new
112
+
113
+ Curses.program do |scr|
114
+ while true
115
+ game.draw_field
116
+ case getch
117
+ when Key::UP
118
+ game.move(:direction => :up, :type => :movement)
119
+ when Key::DOWN
120
+ game.move(:direction => :down, :type => :movement)
121
+ when Key::LEFT
122
+ game.move(:direction => :left, :type => :movement)
123
+ when Key::RIGHT
124
+ game.move(:direction => :right, :type => :movement)
125
+ when 32
126
+ game.mines.uncover_field(game.cursor_x, game.cursor_y)
127
+ when 27, ?q, ?Q
128
+ break
129
+ end
130
+ if game.ended?
131
+ game.draw_field
132
+ setpos(0, 1)
133
+ addstr("Game Over. Hit any key to quit.")
134
+ getch
135
+ break
136
+ end
137
+ end
138
+ close_screen
139
+ puts "Your final score was: #{game.score}"
140
+ end
data/lib/minefield.rb ADDED
@@ -0,0 +1,55 @@
1
+ class MineField
2
+ attr_accessor :fields, :difficulty
3
+
4
+ BEGINNER_WIDTH, BEGINNER_HEIGHT, BEGINNER_MINES = 8, 8, 10
5
+ INTERMEDIATE_WIDTH, INTERMEDIATE_HEIGHT, INTERMEDIATE_MINES = 16, 16, 40
6
+ EXPERT_WIDTH, EXPERT_HEIGHT, EXPERT_MINES = 30, 16, 99
7
+
8
+ def initialize(params={})
9
+ params = { :difficulty => :beginner }.merge params
10
+ @difficulty = params[:difficulty]
11
+ case @difficulty
12
+ when :intermediate: @fields = create_fields(INTERMEDIATE_WIDTH, INTERMEDIATE_HEIGHT, INTERMEDIATE_MINES)
13
+ when :expert: @fields = create_fields( EXPERT_WIDTH, EXPERT_HEIGHT, EXPERT_MINES)
14
+ else @fields = create_fields(BEGINNER_WIDTH, BEGINNER_HEIGHT, BEGINNER_MINES)
15
+ end
16
+ end
17
+
18
+ def create_fields(width, height, mines)
19
+ fields = Array.new(width) { Array.new(height, false) }
20
+ while(mines > 0) do
21
+ x = rand(width)
22
+ y = rand(height)
23
+ unless (fields[x][y] == :mine)
24
+ fields[x][y] = :mine
25
+ mines -= 1
26
+ end
27
+ end
28
+ fields
29
+ end
30
+
31
+ def adjacent_mines(x, y)
32
+ return false if @fields[x][y] == :mine || @fields[x][y] == :boom
33
+ coords = []
34
+ coords << [x-1, y] << [x+1, y] << [x, y-1] << [x, y+1] << [x-1, y-1] << [x-1, y+1] << [x+1, y-1] << [x+1, y+1]
35
+ # reject the coordinates that bleed over the edges of the playfield
36
+ coords = coords.reject do |x, y|
37
+ x < 0 || y < 0 || x > @fields.size - 1 || y > @fields[0].size - 1
38
+ end
39
+ mines = 0
40
+ coords.each do |x, y|
41
+ mines += 1 if @fields[x][y] == :mine || @fields[x][y] == :boom
42
+ end
43
+ mines
44
+ end
45
+
46
+ def uncover_field(x, y)
47
+ field = adjacent_mines(x, y)
48
+ if field
49
+ @fields[x][y] = field
50
+ else
51
+ @fields[x][y] = :boom
52
+ end
53
+ field
54
+ end
55
+ end
@@ -0,0 +1,70 @@
1
+ require "rubygems"
2
+ require "shoulda"
3
+ require "test/unit"
4
+
5
+ require File.dirname(__FILE__) + "/../lib/minefield"
6
+
7
+ class TestMineField < Test::Unit::TestCase
8
+ def setup
9
+ @beginner = MineField.new(:difficulty => :beginner)
10
+ @intermediate = MineField.new(:difficulty => :intermediate)
11
+ @expert = MineField.new(:difficulty => :expert)
12
+ end
13
+
14
+ should "have the MineField class defined" do
15
+ mines = MineField.new
16
+ end
17
+
18
+ should "have a readable attribute :difficulty" do
19
+ assert(MineField.new.difficulty)
20
+ end
21
+
22
+ should "default difficulty to beginner" do
23
+ assert_equal(:beginner, MineField.new.difficulty)
24
+ end
25
+
26
+ should "store difficulties correctly" do
27
+ assert_equal(:beginner, MineField.new(:difficulty => :beginner).difficulty)
28
+ assert_equal(:intermediate, MineField.new(:difficulty => :intermediate).difficulty)
29
+ assert_equal(:expert, MineField.new(:difficulty => :expert).difficulty)
30
+ end
31
+
32
+ should "have the right amount of mines at the difficulty level" do
33
+ assert_equal(10, count_mines(@beginner))
34
+ assert_equal(40, count_mines(@intermediate))
35
+ assert_equal(99, count_mines(@expert))
36
+ assert_equal(30, @expert.fields.size)
37
+ assert_equal(16, @expert.fields[0].size)
38
+ end
39
+
40
+ should "return false if the field is occupied by a mine" do
41
+ @beginner.fields[0][0] = :mine
42
+ assert_equal(:mine, @beginner.fields[0][0])
43
+ assert_equal(false, @beginner.adjacent_mines(0, 0))
44
+ end
45
+ should "calculate adjacent mines for corner fields correctly" do
46
+ @beginner.fields[0][0] = false
47
+ @beginner.fields[0][1] = :mine
48
+ @beginner.fields[1][0] = false
49
+ @beginner.fields[1][1] = false
50
+ @beginner.fields[7][0] = :mine
51
+ assert_equal(1, @beginner.adjacent_mines(0, 0))
52
+ end
53
+
54
+ should "uncover a field" do
55
+ @beginner.uncover_field(3, 4)
56
+ puts ""
57
+ @beginner.fields.each {|column| p column }
58
+ end
59
+
60
+ def count_mines(minefield)
61
+ mines = 0
62
+ minefield.fields.each do |x|
63
+ x.each do |y|
64
+ mines += 1 if y == :mine
65
+ end
66
+ end
67
+ mines
68
+ end
69
+
70
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bloodymines
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - !binary |
14
+ w5Ztw7xyIMOWemtpcg==
15
+
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-08-17 00:00:00 +02:00
21
+ default_executable:
22
+ dependencies: []
23
+
24
+ description: Play a minesweeper-like game in your terminal
25
+ email: darkoem@gmal.com
26
+ executables:
27
+ - bloodymines
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - lib/minefield.rb
34
+ - test/test_minefield.rb
35
+ - bin/bloodymines
36
+ has_rdoc: true
37
+ homepage: http://darkno.de
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A minesweeper-like game.
70
+ test_files: []
71
+