upwords 0.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +250 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/data/ospd.txt +79339 -0
- data/exe/upwords +5 -0
- data/lib/upwords.rb +48 -0
- data/lib/upwords/board.rb +131 -0
- data/lib/upwords/cursor.rb +39 -0
- data/lib/upwords/dictionary.rb +31 -0
- data/lib/upwords/game.rb +325 -0
- data/lib/upwords/graphics.rb +149 -0
- data/lib/upwords/letter_bank.rb +22 -0
- data/lib/upwords/letter_rack.rb +51 -0
- data/lib/upwords/move.rb +98 -0
- data/lib/upwords/move_manager.rb +64 -0
- data/lib/upwords/player.rb +159 -0
- data/lib/upwords/shape.rb +98 -0
- data/lib/upwords/version.rb +3 -0
- data/lib/upwords/word.rb +50 -0
- data/upwords.gemspec +28 -0
- metadata +158 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
module Upwords
|
2
|
+
class Shape
|
3
|
+
|
4
|
+
attr_reader :positions
|
5
|
+
|
6
|
+
def initialize(positions = [])
|
7
|
+
@positions = Set.new
|
8
|
+
positions.reduce(self) {|shape, (row, col)| shape.add(row, col)}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Check if move creates a legal shape when added to a given board.
|
12
|
+
# All checks assume that the move in question has not been played yet.
|
13
|
+
def legal?(board, raise_exception = false)
|
14
|
+
if board.empty? && !in_middle_square?(board)
|
15
|
+
raise IllegalMove, "You must play at least one letter in the middle 2x2 square!" if raise_exception
|
16
|
+
elsif board.empty? && (self.size < board.min_word_length)
|
17
|
+
raise IllegalMove, "Valid words must be at least #{board.min_word_length} letter(s) long!" if raise_exception
|
18
|
+
elsif !straight_line?
|
19
|
+
raise IllegalMove, "The letters in your move must be along a single row or column!" if raise_exception
|
20
|
+
elsif !gaps_covered_by?(board)
|
21
|
+
raise IllegalMove, "The letters in your move must be internally connected!" if raise_exception
|
22
|
+
elsif !(board.empty? || touching?(board))
|
23
|
+
raise IllegalMove, "At least one letter in your move must be touching a previously played word!" if raise_exception
|
24
|
+
elsif covering_moves?(board)
|
25
|
+
raise IllegalMove, "Cannot completely cover up any previously-played words!" if raise_exception
|
26
|
+
else
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
def covering_moves?(board)
|
34
|
+
(board.word_positions).any? do |word_posns|
|
35
|
+
positions >= word_posns
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def gaps_covered_by?(board)
|
40
|
+
row_range.all? do |row|
|
41
|
+
col_range.all? do |col|
|
42
|
+
@positions.include?([row, col]) || board.nonempty_space?(row, col)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def touching?(board)
|
48
|
+
@positions.any? do |row, col|
|
49
|
+
# Are any positions overlapping or adjacent to a non-empty board space
|
50
|
+
[[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]].any? do |dr, dc|
|
51
|
+
board.nonempty_space?(row + dr, col + dc)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def in_middle_square?(board)
|
57
|
+
board.middle_square.any? do |posn|
|
58
|
+
@positions.include?(posn)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def straight_line?
|
63
|
+
row_range.size == 1 || col_range.size == 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def row_range
|
67
|
+
Range.new(*@positions.map {|row, col| row}.minmax)
|
68
|
+
end
|
69
|
+
|
70
|
+
def col_range
|
71
|
+
Range.new(*@positions.map {|row, col| col}.minmax)
|
72
|
+
end
|
73
|
+
|
74
|
+
def add(row, col)
|
75
|
+
if row.is_a?(Integer) && col.is_a?(Integer)
|
76
|
+
@positions.add [row, col]
|
77
|
+
self
|
78
|
+
else
|
79
|
+
raise ArgumentError, "[#{row}, #{col}] is not a valid position]"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
alias_method :<<, :add
|
84
|
+
|
85
|
+
def empty?
|
86
|
+
@positions.empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
def size
|
90
|
+
@positions.size
|
91
|
+
end
|
92
|
+
|
93
|
+
def include? (row, col)
|
94
|
+
@positions.include? [row, col]
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/lib/upwords/word.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Upwords
|
2
|
+
class Word
|
3
|
+
|
4
|
+
# MIN_WORD_LENGTH = 2
|
5
|
+
|
6
|
+
attr_reader :score, :length
|
7
|
+
|
8
|
+
def initialize(posns, board)
|
9
|
+
posns = posns.uniq if posns.is_a?(Array)
|
10
|
+
@text = Word.make_string(board, posns)
|
11
|
+
@score = Word.calc_score(board, posns)
|
12
|
+
@length = @text.length
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@text.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def legal?(dict)
|
20
|
+
dict.legal_word?(self.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
# A word's score is the sum of the tile heights of its letters
|
24
|
+
# However, if all of a word's tile heights are exactly 1, then the score is double the word's length
|
25
|
+
# also add two points for each 'Qu' if all words are 1 tile high
|
26
|
+
# Add 20 points if a player uses all their letters to form a word (this logic is in the MoveManager class,
|
27
|
+
# because it requires the player as an input
|
28
|
+
def self.calc_score(board, posns)
|
29
|
+
stack_heights = posns.map{|row, col| board.stack_height(row, col)}
|
30
|
+
|
31
|
+
score = stack_heights.inject(0) {|sum, h| sum + h}
|
32
|
+
|
33
|
+
# Double score if all letters are only 1 tile high
|
34
|
+
if stack_heights.all? {|h| h == 1}
|
35
|
+
score *= 2
|
36
|
+
|
37
|
+
# Add two points for each Qu (only all letters 1 tile high)
|
38
|
+
score += (2 * posns.count {|posn| board.top_letter(*posn) == 'Qu'})
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO: Add 20 points if a player uses all of their entire rack in one turn. 7 is the maximum rack capacity
|
42
|
+
score
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.make_string(board, posns)
|
46
|
+
posns.map{|row, col| board.top_letter(row, col)}.join
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/upwords.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'upwords/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "upwords"
|
9
|
+
s.version = Upwords::VERSION
|
10
|
+
s.author = "Maxim Pertsov"
|
11
|
+
s.email = "maxim.pertsov@gmail.com"
|
12
|
+
s.summary = %q{Command-line version of the Upwords boardgame. Upwords is similar to Scrabble, except you can stack new letters on top of previously-played letters. Play with 1 to 4 human or computer players.}
|
13
|
+
s.description = %q{Command-line version of the Upwords boardgame. Upwords is similar to Scrabble, except you can stack new letters on top of previously-played letters. Play with 1 to 4 human or computer players.}
|
14
|
+
s.homepage = "https://github.com/maximpertsov/upwords"
|
15
|
+
s.license = "MIT"
|
16
|
+
|
17
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
s.bindir = "exe"
|
19
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib", "data"]
|
21
|
+
|
22
|
+
s.add_dependency "curses", "~> 1.0"
|
23
|
+
s.add_development_dependency "bundler", "~> 1.11"
|
24
|
+
s.add_development_dependency "rake", "~> 10.0"
|
25
|
+
s.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
s.add_development_dependency "minitest-reporters", "~> 1.1"
|
27
|
+
s.add_development_dependency "pry", "~> 0.10"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: upwords
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maxim Pertsov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: curses
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.11'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.11'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-reporters
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.10'
|
97
|
+
description: Command-line version of the Upwords boardgame. Upwords is similar to
|
98
|
+
Scrabble, except you can stack new letters on top of previously-played letters.
|
99
|
+
Play with 1 to 4 human or computer players.
|
100
|
+
email: maxim.pertsov@gmail.com
|
101
|
+
executables:
|
102
|
+
- upwords
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".travis.yml"
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/console
|
113
|
+
- bin/setup
|
114
|
+
- data/ospd.txt
|
115
|
+
- exe/upwords
|
116
|
+
- lib/upwords.rb
|
117
|
+
- lib/upwords/board.rb
|
118
|
+
- lib/upwords/cursor.rb
|
119
|
+
- lib/upwords/dictionary.rb
|
120
|
+
- lib/upwords/game.rb
|
121
|
+
- lib/upwords/graphics.rb
|
122
|
+
- lib/upwords/letter_bank.rb
|
123
|
+
- lib/upwords/letter_rack.rb
|
124
|
+
- lib/upwords/move.rb
|
125
|
+
- lib/upwords/move_manager.rb
|
126
|
+
- lib/upwords/player.rb
|
127
|
+
- lib/upwords/shape.rb
|
128
|
+
- lib/upwords/version.rb
|
129
|
+
- lib/upwords/word.rb
|
130
|
+
- upwords.gemspec
|
131
|
+
homepage: https://github.com/maximpertsov/upwords
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
- data
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.5.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Command-line version of the Upwords boardgame. Upwords is similar to Scrabble,
|
156
|
+
except you can stack new letters on top of previously-played letters. Play with
|
157
|
+
1 to 4 human or computer players.
|
158
|
+
test_files: []
|