bloodymines 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+