1
# -*- coding: utf-8 -*-
4
# Time-stamp: <2007-08-22 01:26:11 bruno>
6
# Copyright (C) 2005 Joe Neeman
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 3 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, see <http://www.gnu.org/licenses/>.
36
from gcompris import gcompris_gettext as _
39
def __init__(self, text, good):
44
def __init__(self, x, y):
46
self.pic = game.rootitem.add( gnomecanvas.CanvasText,
48
font = gcompris.skin.get_font("gcompris/content"),
52
def setNum(self, num):
55
self.pic.set( text = num.text )
57
self.pic.set( text = "" )
60
def __init__(self, title, numlist):
62
self.numbers = numlist
64
# the following class goes unused: is is only here to document the Levelset interface.
69
def getError(self, num):
75
def setLevel(self, level, sublevel):
84
for i in range(2, int(math.sqrt(n) + 1) ):
89
def makeNumList(nums):
94
for i in range(1, len(nums)-1):
101
for i in range(1, n/2 + 1):
110
self.num_sublevels = 9
111
self.level_max = [ 3, 5, 7, 11, 13, 17, 19, 23, 29 ]
113
self.cur_sublevel = 1
115
def getError(self, num):
116
fmt = _('%d is divisible by %s.')
120
return _("1 is not a prime number.")
123
for i in range(2, n/2 + 1):
126
s = makeNumList(factors) % tuple(factors)
130
return _('Primes less than %d') % ( self.level_max[self.cur_sublevel-1] + 1 )
132
def setLevel(self, level, sublevel):
133
self.cur_sublevel = level
134
self.cur_sublevel = sublevel
137
n = random.randint( 1, self.level_max[self.cur_sublevel-1] )
138
return Number( str(n), isPrime(n) )
140
class FactorLevelset:
142
self.num_sublevels = 9
144
self.level_multiple = [ 4, 6, 8, 10, 12, 15, 18, 20, 24 ]
146
self.cur_sublevel = 1
150
def getError(self, num):
151
# Translators: You can swap %(x)y elements in the string.
152
fmt = _('Multiples of %(d1)d include %(s)s,\nbut %(d2)d is not a multiple of %(d3)d.')
155
for i in range(2, 5):
157
return fmt % { 'd1': n,
158
's': makeNumList(mults) % tuple(mults),
159
'd2': self.level_multiple[self.cur_sublevel-1],
163
return _('Factors of %d') % ( self.level_multiple[self.cur_sublevel-1] )
165
def setLevel(self, level, sublevel):
166
self.curlevel = level
167
self.cur_sublevel = sublevel
170
for i in range(1, self.level_multiple[sublevel-1]+1):
171
if self.level_multiple[sublevel-1] % i == 0:
172
self.factors.append(i)
174
self.nonfactors.append(i)
177
if random.randint(0,1):
178
# choose a good number
179
n = self.factors[ random.randint(0, len(self.factors)-1) ]
180
num = Number( str(n), 1 )
182
# choose a wrong number
183
n = self.nonfactors[ random.randint(0, len(self.nonfactors)-1) ]
184
num = Number( str(n), 0 )
187
class MultipleLevelset:
190
self.num_sublevels = 9
193
self.cur_sublevel = 1
195
def getError(self, num):
196
fmt = _('%s are the factors of %d.')
200
for i in range(1, n/2+1):
204
s = makeNumList(factors) % tuple(factors)
208
return _('Multiples of %d') % ( self.cur_sublevel+1 )
210
def setLevel(self, level, sublevel):
211
self.curlevel = level
212
self.cur_sublevel = sublevel
215
if random.randint(0,1):
216
# choose a good number
217
n = (self.cur_sublevel+1) * random.randint(1, self.min_mult + self.curlevel*2)
218
num = Number( str(n), 1 )
220
# choose a wrong number
221
n = (self.cur_sublevel+1) * random.randint(1, self.min_mult + self.curlevel*2) - random.randint(1, self.cur_sublevel)
222
num = Number( str(n), 0 )
225
# for all expression-based levels, we add a value field to the Number
226
class ExpressionLevelset(object):
229
self.num_sublevels = 7
230
self.levelops = [ [self.getPlus],
232
[self.getPlus, self.getMinus],
234
[self.getPlus, self.getMinus, self.getTimes],
236
[self.getPlus, self.getMinus, self.getTimes, self.getDivide]
239
self.cur_sublevel = 1
241
def getError(self, num):
243
return fmt % (num.text, num.value)
245
def getNumberWithAnswer(self, answer):
246
fn = random.choice( self.levelops[self.curlevel-1] )
251
def getPlus(self, answer):
252
n = random.randint(0, answer)
253
num = Number( _(u'%d + %d') % (n, answer-n), 1 )
256
def getMinus(self, answer):
257
n = random.randint(answer, answer*2)
258
num = Number( _(u'%d \u2212 %d') % (n, n-answer), 1 )
261
def getTimes(self, answer):
262
n = random.choice( getFactors(answer) )
263
return Number( _(u'%d \u00d7 %d') % (n, answer/n), 1 )
265
def getDivide(self, answer):
266
n = random.randint(1, 5)
267
return Number( _(u'%d \u00f7 %d') % (answer*n, n), 1 )
269
class EqualityLevelset(ExpressionLevelset):
271
super(EqualityLevelset, self).__init__()
275
return _('Equal to %d') % (self.answer,)
277
def setLevel(self, level, sublevel):
278
self.curlevel = level
279
self.cur_sublevel = sublevel
280
self.answer = self.answermin + self.cur_sublevel
283
if random.randint(0, 1):
285
num = self.getNumberWithAnswer(self.answer)
289
ans = random.choice( range(self.answermin, self.answer) + range(self.answer+1, self.answer*2) )
290
num = self.getNumberWithAnswer(ans)
294
class InequalityLevelset(EqualityLevelset):
296
return _('Not equal to %d') % (self.answer,)
299
num = super(InequalityLevelset, self).getNumber()
306
class Player(object):
309
self.move_stepnum = 0
310
self.x = self.y = self.x_old = self.y_old = -1
311
self.moving = self.exists = False
312
self.action_start = 0
316
self.movestep_timer = 0
322
# These are defined in Muncher and Troggle
327
if self.movestep_timer != 0:
328
gobject.source_remove(self.movestep_timer)
329
self.movestep_timer = 0
330
if self.munch_timer != 0:
331
gobject.source_remove(self.munch_timer)
337
def isAt(self, x, y):
338
return self.exists and not self.moving and (self.x == x and self.y == y)
340
def isNear(self, x, y):
341
return self.exists and ( (self.x == x and self.y == y)
342
or (self.moving and self.x_old == x
347
if self.move_stepnum < game.num_moveticks-1:
348
self.move_stepnum += 1
349
x_old = self.anim.gnomecanvas.get_property("x")
350
y_old = self.anim.gnomecanvas.get_property("y")
351
x = self.anim.gnomecanvas.get_property("x") + self.velocity[0]*game.sw/game.num_moveticks
352
y = self.anim.gnomecanvas.get_property("y") + self.velocity[1]*game.sh/game.num_moveticks
355
self.move_stepnum = 0
356
x = game.sw * self.x + game.left
357
y = game.sh * self.y + game.top
359
self.movestep_timer = 0
362
self.anim.gnomecanvas.set(x=x, y=y)
365
def move(self, x_old, y_old, x, y):
366
gcompris.sound.play_ogg("sounds/smudge.wav")
371
self.velocity = [x-x_old, y-y_old]
372
self.anim.gnomecanvas.set(x=(self.x_old * game.sw + game.left),
373
y=(self.y_old * game.sh + game.top))
376
# it takes game.num_moveticks iterations of duration game.move_tick to move squares
377
if x != x_old or y != y_old:
378
self.anim.setState(1)
379
self.movestep_timer = game.timeout_add(game.move_tick, self.move_step)
383
def startMunching(self):
384
gcompris.sound.play_ogg("sounds/eat.wav")
385
self.anim.setState(2)
386
self.munch_timer = game.timeout_add(game.munch_time, self.stopMunching)
389
def stopMunching(self):
391
self.anim.setState(0)
395
self.anim.setState(0)
398
# work out eating stuff
399
for p in game.players:
400
if p.isAt(self.x, self.y) and p != self:
401
if self.foodchain >= p.foodchain:
409
class Muncher(Player):
411
super(Muncher, self).__init__()
413
self.anim = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
414
self.spare = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
415
self.anim.gnomecanvas.hide()
417
self.spare.gnomecanvas.set(x=0, y=0)
421
self.spare.gnomecanvas.show()
422
elif self.lives == 0:
423
self.spare.gnomecanvas.hide()
430
self.anim.gnomecanvas.show()
433
super(Muncher, self).die()
436
self.anim.gnomecanvas.hide()
440
game.show_message( _("You were eaten by a Troggle.\nPress <Return> to continue.") )
443
def push_key(self, key):
446
self.key_queue.append(key)
447
elif len(self.key_queue) == 0:
450
self.key_queue.append(key)
451
self.handle_key(self.key_queue.pop(0))
453
if key == gtk.keysyms.Return:
456
def handle_key(self, key):
457
if key == gtk.keysyms.Left:
459
self.move(self.x, self.y, (self.x-1), self.y)
462
elif key == gtk.keysyms.Right:
463
if self.x < game.width - 1:
464
self.move(self.x, self.y, (self.x+1), self.y)
467
elif key == gtk.keysyms.Up:
469
self.move(self.x, self.y, self.x, (self.y-1))
472
elif key == gtk.keysyms.Down:
473
if self.y < game.height - 1:
474
self.move(self.x, self.y, self.x, (self.y+1))
477
elif key == gtk.keysyms.space:
479
if len( self.key_queue ) > 0: # we don't need to wait for munching to finish to start the next action
480
self.handle_key( self.key_queue.pop(0) )
483
num = game.squares[self.x][self.y].num
489
game.show_message( _("You ate a wrong number.\n") +game.levelset.getError(num) +
490
_("\nPress <Return> to continue.") )
492
game.setNum(self.x, self.y, None)
495
super(Muncher, self).stop()
496
if len(self.key_queue) > 0:
497
key = self.key_queue.pop(0)
501
class Troggle(Player):
503
super(Troggle, self).__init__()
504
self.anim = gcompris.anim.CanvasItem(game.munchanimation, game.rootitem)
506
self.nextspawn_timer = 0
507
self.nextmove_timer = 0
513
self.nextspawn_timer = 0
516
index = random.randint(0, (len( game.troganimation )-1) * game.board.sublevel / game.board.number_of_sublevel)
517
self.anim.swapAnimation(game.troganimation[index])
518
self.getMove = game.trogmoves[index]
519
self.onMove = game.onmove[index]
520
self.onStop = game.onstop[index]
521
if random.randint(0,1) == 0:
522
if random.randint(0,1) == 0:
526
self.x_old = game.width
527
self.x = game.width - 1
528
self.y = self.y_old = random.randint(0, game.height-1)
530
if random.randint(0,1) == 0:
534
self.y_old = game.height
535
self.y = game.height - 1
536
self.x = self.x_old = random.randint(0, game.width-1)
537
self.move(self.x_old, self.y_old, self.x, self.y)
538
self.anim.gnomecanvas.show()
539
game.hide_trogwarning()
542
super(Troggle, self).die()
544
self.anim.gnomecanvas.hide()
546
time = game.trog_spawn_time()
547
self.nextspawn_timer = game.timeout_add( time + game.trogwarn_time, self.spawn )
548
self.warn_timer = game.timeout_add( time, game.show_trogwarning )
549
if self.nextmove_timer != 0:
550
gobject.source_remove(self.nextmove_timer)
551
self.nextmove_timer = 0
556
def move(self, a, b, c, d):
557
if self.onMove != None and (a != c or b != d) and game.onBoard(a, b):
561
super(Troggle, self).move(a, b, c, d)
565
if self.x < 0 or self.x >= game.width or self.y < 0 or self.y >= game.height:
568
super(Troggle, self).stop()
569
self.nextmove_timer = game.timeout_add(game.trog_wait, self.getTrogMove)
570
if self.onStop != None:
573
def getTrogMove(self):
574
self.nextmove_timer = 0
577
x, y = self.getMove(self)
578
self.move(x_old, y_old, x, y)
580
# the troggle move types
581
def trogMove_straight(self):
582
x = self.x + (self.x - self.x_old)
583
y = self.y + (self.y - self.y_old)
586
def trogMove_random(self):
589
r = random.randint(0,3)
590
if r >= 1: # move straight
591
x += self.x - self.x_old
592
y += self.y - self.y_old
593
elif r == 2: # turn left
594
x += self.y - self.y_old
595
y -= self.x - self.x_old
597
x -= self.y - self.y_old
598
y += self.x - self.x_old
601
def trogMove_chase(self):
604
dx = game.muncher.x - x
605
dy = game.muncher.y - y
606
if dx == 0 and dy == 0:
607
return self.trogMove_straight()
608
if not game.muncher.exists or abs(dx) > game.width/2 or abs(dy) > game.height/2:
609
return self.trogMove_straight()
610
if abs(dx) > abs(dy) or (abs(dx) == abs(dy) and random.randint(0,1) == 0):
616
def trogMove_run(self):
619
dx = game.muncher.x - x
620
dy = game.muncher.y - y
621
if not game.muncher.exists or abs(dx) > game.width/2 or abs(dy) > game.height/2:
622
return self.trogMove_random()
623
if abs(dx) > abs(dy) or (abs(dx) == abs(dy) and random.randint(0,1) == 0):
629
def onMove_create(self):
630
game.setNum( self.x, self.y, game.levelset.getNumber() )
632
def onStop_munch(self):
633
if game.squares[self.x][self.y].num != None:
635
game.setNum( self.x, self.y, None )
637
class Gcompris_gnumch:
638
def __init__(self, board):
643
self.board.disable_im_context = True
644
self.scrw = gcompris.BOARD_WIDTH
645
self.scrh = gcompris.BOARD_HEIGHT
649
self.sw = self.scrw / (self.width + 1)
650
self.sh = self.scrh / (self.width + 1)
651
self.left = self.scrw - (self.sw * self.width)
652
self.top = self.scrh - (self.sh * self.height)
654
self.munchanimation = gcompris.anim.Animation("gnumch/muncher.txt")
655
self.troganimation = []
656
self.trogmoves = [Troggle.trogMove_straight, Troggle.trogMove_random,
657
Troggle.trogMove_run, Troggle.trogMove_random,
658
Troggle.trogMove_chase]
659
self.onmove = [ None, Troggle.onMove_create, None, None, None ]
660
self.onstop = [ None, None, None, Troggle.onStop_munch, None ]
661
for file in [ "gnumch/reggie.txt",
665
"gnumch/smarty.txt" ]:
666
self.troganimation.append( gcompris.anim.Animation(file) )
671
if board.mode == "primes":
672
self.levelset = PrimeLevelset()
673
elif board.mode == "factors":
674
self.levelset = FactorLevelset()
675
elif board.mode == "multiples":
676
self.levelset = MultipleLevelset()
677
elif board.mode == "equality":
678
self.levelset = EqualityLevelset()
679
elif board.mode == "inequality":
680
self.levelset = InequalityLevelset()
682
print "Warning: no levelset type specified, defaulting to primes"
683
self.levelset = PrimeLevelset()
685
print "Gcompris_gnumch __init__."
689
self.num_moveticks = 10
690
self.munch_time = 400
691
self.trog_wait = 1000
692
self.trogwarn_time = 1000
693
self.trogspawn_min = 3000
694
self.trogspawn_max = 10000
698
self.board.maxlevel = self.levelset.numlevels
699
self.board.sublevel = 1
700
self.board.number_of_sublevel = self.levelset.num_sublevels
701
self.trog_wait = 1900
704
gcompris.set_background(self.board.canvas.root(), gcompris.skin.image_to_skin("gcompris-bg.jpg"))
705
gcompris.bar_set_level(self.board)
707
pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png"))
709
gcompris.bar_set_repeat_icon(pixmap)
710
gcompris.bar_set(gcompris.BAR_LEVEL | gcompris.BAR_REPEAT_ICON)
712
gcompris.bar_set(gcompris.BAR_LEVEL | gcompris.BAR_REPEAT)
714
# create our rootitem. We put each canvas item here so at the end we only
715
# need to destroy the rootitem
716
self.rootitem = self.board.canvas.root().add(gnomecanvas.CanvasGroup,
720
# draw the board on top of the background
721
for i in range(0,self.width+1):
722
self.rootitem.add(gnomecanvas.CanvasLine,
723
points = (i*self.sw + self.left, self.top,
724
i*self.sw + self.left, self.scrh),
725
fill_color_rgba = 0x000000FFL,
727
for i in range(0,self.height+1):
728
self.rootitem.add(gnomecanvas.CanvasLine,
729
points = (self.left, self.top + i*self.sh,
730
self.scrw, self.top + i*self.sh),
731
fill_color_rgba = 0x000000FFL,
734
# munchers and troggles
736
self.muncher = Muncher()
737
self.troggles = [Troggle(), Troggle(), Troggle()]
739
self.players[:] = self.troggles
740
self.players.append(self.muncher)
744
for i in range(0, self.width):
746
for j in range(0, self.height):
747
s = Square(self.left + self.sw*i + self.sw/2, self.top + self.sh*j + self.sh/2)
750
self.squares.append(tmp)
752
# so that the troggles get clipped to the board area
753
self.rootitem.add(gnomecanvas.CanvasRect,
755
x2=self.scrw, y2=self.top,
756
fill_color_rgba = 0xFFFFFFFFL)
757
self.rootitem.add(gnomecanvas.CanvasRect,
759
x2=self.left, y2=self.scrh,
760
fill_color_rgba = 0xFFFFFFFFL)
763
self.title = self.rootitem.add(gnomecanvas.CanvasText,
765
font = gcompris.skin.get_font("gcompris/board/title bold"),
770
self.message_back = self.rootitem.add(gnomecanvas.CanvasRect,
771
x1=0, y1=0, x2=1, y2=1,
772
fill_color_rgba = 0x60F06060L)
773
self.message = self.rootitem.add(gnomecanvas.CanvasText,
775
justification = gtk.JUSTIFY_CENTER,
776
font = gcompris.skin.get_font("gcompris/board/title bold"),
782
self.trogwarning = self.rootitem.add(gnomecanvas.CanvasText,
783
text = _("T\nR\nO\nG\nG\nL\nE"),
784
justification = gtk.JUSTIFY_CENTER,
785
font = gcompris.skin.get_font("gcompris/board/title bold"),
788
self.trogwarning.hide()
789
self.trogwarning_num = 0
792
self.muncher.spare.gnomecanvas.raise_to_top()
796
def show_trogwarning(self):
797
self.trogwarning_num += 1
798
if self.trogwarning_num == 1:
799
self.trogwarning.show()
801
def hide_trogwarning(self):
802
self.trogwarning_num -= 1
803
if self.trogwarning_num == 0:
804
self.trogwarning.hide()
806
def show_message(self, text):
807
self.message.set( text = text )
808
w = self.message.get_property("text-width")
809
h = self.message.get_property("text-height")
810
self.message_back.set( x1 = (self.scrw - w)/2, y1 = (self.scrh - h)/2,
811
x2 = (self.scrw + w)/2, y2 = (self.scrh + h)/2 )
812
self.message_back.show()
815
def hide_message(self):
817
self.message_back.hide()
819
def set_level(self, level):
820
self.board.level = level;
821
self.board.sublevel = 1;
822
gcompris.bar_set_level(self.board);
823
self.trog_wait = 2000 - self.board.level*100
827
def trog_spawn_time(self):
828
return random.randint(self.trogspawn_min, self.trogspawn_max)
830
def setNum(self, x, y, new):
832
if new == None or not new.good:
836
if self.squares[x][y].num == None or not self.squares[x][y].num.good:
842
self.goodies += change
843
if self.goodies == 0:
845
self.squares[x][y].setNum(new)
847
def key_press(self, keyval, commit_str, preedit_str):
848
self.muncher.push_key(keyval)
853
if self.muncher.munch_timer != 0:
854
gobject.source_remove(self.muncher.munch_timer)
855
if self.muncher.movestep_timer != 0:
856
gobject.source_remove(self.muncher.movestep_timer)
858
for t in self.troggles:
859
for timer in [t.munch_timer, t.movestep_timer, t.nextmove_timer, t.nextspawn_timer, t.warn_timer]:
861
gobject.source_remove(timer)
865
self.levelset.setLevel(self.board.level, self.board.sublevel)
866
self.title.set(text = self.levelset.getTitle())
867
self.trogwarning_num = 1
868
self.hide_trogwarning()
871
for col in self.squares:
873
s.setNum( self.levelset.getNumber() )
877
self.muncher.lives = 1
878
for i in range(0, len(self.troggles)):
879
if i < self.board.sublevel-1:
880
self.troggles[i].die()
881
else: # don't move them into the spawning queue
882
self.troggles[i].exists = 0
883
self.troggles[i].anim.gnomecanvas.hide()
889
gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.TUX)
893
gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.TUX)
895
def onBoard(self, x, y):
896
return x >= 0 and x < self.width and y >= 0 and y < self.height
903
# if we are paused, then unpaused it means that they beat the sublevel
904
self.increment_level()
906
self.set_level(self.board.level)
908
def increment_level(self):
909
self.board.sublevel += 1
910
if self.board.sublevel > self.board.number_of_sublevel:
911
self.set_level( self.board.level % self.board.maxlevel + 1)
920
def timeout_add(self, t, fn):
921
if not self.paused and not self.stopped:
922
return gobject.timeout_add(t, fn)
927
for i in range(0, len(self.troggles)):
928
self.troggles[i].anim.destroy()
929
if self.muncher.anim:
930
self.muncher.anim.destroy()
932
self.rootitem.destroy()