1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# *** CONVERTED FROM THE ORIGINAL FOCAL PROGRAM AND MODIFIED
# *** FOR EDUSYSTEM 70 BY DAVID AHL, DIGITAL
# *** MODIFIED FOR 8K MICROSOFT BASIC BY PETER TURNBULL
# *** RUBY PORT BY MARTIN DEMELLO

class AdvisorException < Exception; end

class Hamurabi
  attr_accessor :year, :population, :acres, :grain, :exchange
  attr_accessor :stats, :starved, :inc, :rats, :harvest

  def initialize
    @stats = {:total => 0, :avg => 0} # starvation statistics
    @year = 0
    @population = 95
    @grain = 2800
    @rats = 200
    @acres = 1000
    @starved = 0
    @inc = 5
    @harvest = 3
  end

  def intro
    puts "TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA"
    puts "SUCCESSFULLY FOR A 10-YR TERM OF OFFICE."
    puts
  end

  def roll
    rand(5) + 1
  end

  def update
    self.year += 1
    self.exchange = rand(10) + 17
    self.population += inc
    puts
    puts
    puts "HAMURABI:  I BEG TO REPORT TO YOU,"
    puts "IN YEAR #{year}, #{starved} PEOPLE STARVED, #{inc} CAME TO THE CITY."
    puts "POPULATION IS NOW #{population}" 
    puts "THE CITY NOW OWNS #{acres} ACRES."
    puts "YOU HARVESTED #{harvest} BUSHELS PER ACRE."
    puts "RATS ATE #{rats} BUSHELS."
    puts "YOU NOW HAVE #{grain} BUSHELS IN STORE."
    puts
  end

  def cycle
    update

    # make decisions
    trade_land
    fed = allocate_food / 20
    planted = plant_grain

    # throw the dice
    self.harvest = roll
    crop = planted * harvest
    c = roll
    self.rats = c % 2 == 0 ? grain/c : 0
    self.grain = grain - rats + crop 

    self.inc = roll*(20*acres+grain)/population/100+1
    plague if rand < 0.15
    starved = population < fed ? 0 : population - fed
    impeached!(starved) if starved > population * 0.45
    stats[:avg] =((year - 1) * stats[:avg] + starved * 100.0/population)/year
    stats[:total] += starved
  end

  def plague
    self.population /= 2
    puts "A HORRIBLE PLAGUE STRUCK!  HALF THE PEOPLE DIED."
  end

  # decisions
  def refuse
    puts
    puts "HAMURABI:  I CANNOT DO WHAT YOU WISH."
    puts "GET YOURSELF ANOTHER STEWARD!!!!!"
    bye
  end

  def get_num(prompt)
    print "#{prompt}: "
    i = gets.to_i
    refuse if i < 0
    i
  end

  def action
    while true
      begin
        return yield
      rescue AdvisorException => e
        puts e.to_s + " NOW, THEN,"
      end
    end
  end

  def check(cond, msg)
    raise AdvisorException.new(msg) unless cond
  end

  def out_of_grain_error
    return "HAMURABI:  THINK AGAIN. YOU HAVE ONLY #{grain} BUSHELS OF GRAIN."
  end

  def sell_land
    action do
      q = get_num(%{HOW MANY ACRES DO YOU WISH TO SELL})
      check(q < acres, "HAMURABI:  THINK AGAIN. YOU OWN ONLY #{acres} ACRES.")
      self.acres -= q
      self.grain += exchange*q
    end
  end

  def trade_land
    puts "LAND IS TRADING AT #{exchange} BUSHELS PER ACRE."
    action do
      q = get_num("HOW MANY ACRES DO YOU WISH TO BUY")
      return sell_land if q == 0
      check(exchange * q <= grain, out_of_grain_error)
      self.acres += q
      self.grain -= exchange*q
    end
  end

  def allocate_food
    action do
      q = get_num("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE")
      check(q <= grain, out_of_grain_error)
      self.grain -= q
      q
    end
  end

  def plant_grain
    action do
      d = get_num("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED")
      return 0 if d == 0
      check(d <= acres, "HAMURABI:  THINK AGAIN. YOU OWN ONLY #{acres} ACRES.")
      check(d/2 < grain, out_of_grain_error)
      check(d < 10*population, "BUT YOU HAVE ONLY #{population} PEOPLE TO TEND THE FIELDS.")
      self.grain -= d/2
      d
    end
  end

  def score
    l = acres/population
    a = stats[:avg]
    puts "IN YOUR 10-YEAR TERM OF OFFICE, #{a} PERCENT OF THE"
    puts "POPULATION STARVED PER YEAR ON AVERAGE, I.E., A TOTAL OF"
    puts "#{stats[:total]} PEOPLE DIED!!"
    puts "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH"
    puts "#{l} ACRES PER PERSON."
    puts
    case true
    when (a > 33 or l < 7) then impeach!
    when (a > 10 or l < 9) then lose
    when (a > 3 or l < 10) then bleh
    else win
    end
  end


  def win
    puts "A FANTASTIC PERFORMANCE!!!  CHARLEMANGE, DISRAELI, AND"
    puts "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!"
  end

  def lose
    puts "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV."
    puts "THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND,"
    puts "FRANKLY, HATE YOUR GUTS!"
  end

  def bleh
    puts "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT"
    puts "REALLY WASN'T TOO BAD AT ALL. ";
    puts "#{rand(population*8/10)} PEOPLE WOULD"
    puts "DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR"
    puts "TRIVIAL PROBLEMS."
  end

  def impeached!(starved = nil)
    puts
    puts "YOU STARVED #{starved} PEOPLE IN ONE YEAR!!!" if starved
    puts "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"
    puts "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"
    puts "ALSO BEEN DECLARED 'NATIONAL FINK' !!"
    bye
  end

  def bye
    10.times { print 7.chr }
    puts "SO LONG FOR NOW."
    exit
  end

end

# main loop

game = Hamurabi.new
game.intro
10.times { game.cycle }
game.score
bye