1
# gcompris - lightsoff.py
3
# Copyright (C) 2010 Bruno and Clement Coudoin
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, see <http://www.gnu.org/licenses/>.
18
# lightsoff Board module
27
from gcompris import gcompris_gettext as _
29
class Gcompris_lightsoff:
30
"""Empty gcompris python class"""
33
def __init__(self, gcomprisBoard):
34
# Save the gcomprisBoard, it defines everything we need
35
# to know from the core
36
self.gcomprisBoard = gcomprisBoard
40
# These are used to let us restart only after the bonus is displayed.
41
# When the bonus is displayed, it call us first with pause(1) and then with pause(0)
42
self.board_paused = False;
383
self.gcomprisBoard.level=1
384
self.gcomprisBoard.maxlevel=len(self.data)
385
self.gcomprisBoard.sublevel=1
386
self.gcomprisBoard.number_of_sublevel=1
388
# Create our rootitem. We put each canvas item in it so at the end we
389
# only have to kill it. The canvas deletes all the items it contains
391
self.backroot = goocanvas.Group(parent = \
392
self.gcomprisBoard.canvas.get_root_item())
394
# A color changing background
395
self.background = goocanvas.Rect(
396
parent = self.backroot,
399
width = gcompris.BOARD_WIDTH,
401
fill_color_rgba = 0xFFFFFFFFL
404
svghandle = gcompris.utils.load_svg("lightsoff/back.svgz")
406
parent = self.backroot,
407
svg_handle = svghandle,
408
svg_id = "#BACKGROUND",
409
pointer_events = goocanvas.EVENTS_NONE
413
self.sunitem = goocanvas.Svg(
414
parent = self.backroot,
415
svg_handle = svghandle,
418
self.sunitem_offset = 0
421
self.tuxitem = goocanvas.Svg(
422
parent = self.backroot,
423
svg_handle = svghandle,
426
self.tuxitem.connect("button_press_event", self.solve_event)
427
gcompris.utils.item_focus_init(self.tuxitem, None)
430
self.bubbleitem = goocanvas.Svg(
431
parent = self.backroot,
432
svg_handle = svghandle,
436
self.textitem = goocanvas.Text(
437
parent = self.backroot,
438
x = gcompris.BOARD_WIDTH/2 + 80,
439
y = gcompris.BOARD_HEIGHT - 80,
441
font = gcompris.skin.get_font("gcompris/content"),
442
text = _("Switch off all the lights, I have to go to sleep.\n"
443
"If you need help, click on me."),
444
fill_color = "black",
445
anchor = gtk.ANCHOR_CENTER
449
self.tipiitem = goocanvas.Svg(
450
parent = self.backroot,
451
svg_handle = svghandle,
455
# The image foreground
457
parent = self.backroot,
458
svg_handle = svghandle,
459
svg_id = "#FOREGROUND",
460
pointer_events = goocanvas.EVENTS_NONE
463
# Set the buttons we want in the bar
464
gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT)
465
gcompris.bar_location(gcompris.BOARD_WIDTH/2 - 90, -1, 0.6)
466
gcompris.bar_set_level(self.gcomprisBoard)
471
self.backroot.remove()
477
print("lightsoff ok.")
484
print("lightsoff config.")
487
def key_press(self, keyval, commit_str, preedit_str):
490
def pause(self, pause):
491
self.board_paused = pause
493
# When the bonus is displayed, it call us first
494
# with pause(1) and then with pause(0)
496
if(self.gamewon == True and pause == False):
498
if(self.increment_level()):
503
def set_level(self, level):
504
self.gcomprisBoard.level = level
505
gcompris.bar_set_level(self.gcomprisBoard)
509
# Code that increments the sublevel and level
510
# And bail out if no more levels are available
511
# return 1 if continue, 0 if bail out
512
def increment_level(self):
513
self.gcomprisBoard.sublevel += 1
515
if(self.gcomprisBoard.sublevel>self.gcomprisBoard.number_of_sublevel):
517
self.gcomprisBoard.sublevel=1
518
self.gcomprisBoard.level += 1
519
gcompris.bar_set_level(self.gcomprisBoard)
521
if(self.gcomprisBoard.level>self.gcomprisBoard.maxlevel):
522
self.gcomprisBoard.level = self.gcomprisBoard.maxlevel
526
def create_empty_list(self):
529
items.append(range(5))
530
for y in range(len(items)):
531
for x in range(len(items[0])):
535
# Display the board game
536
def display_game(self):
537
# The grid we display
538
# It contains all the graphic items
539
self.items = self.create_empty_list()
541
# The grid of hints items
542
self.hints = self.create_empty_list()
544
# Do we display the hints
545
self.hints_mode = False
548
self.rootitem.remove()
550
self.tipiitem.props.visibility = goocanvas.ITEM_INVISIBLE
551
self.textitem.props.visibility = goocanvas.ITEM_VISIBLE
552
self.tuxitem.props.visibility = goocanvas.ITEM_VISIBLE
553
self.bubbleitem.props.visibility = goocanvas.ITEM_VISIBLE
555
# Create our rootitem. We put each canvas item in it so at the end we
556
# only have to kill it. The canvas deletes all the items it contains
559
goocanvas.Group(parent = self.backroot)
561
svghandle = gcompris.utils.load_svg("lightsoff/onoff.svgz")
562
iwidth = svghandle.props.width
563
iheight = svghandle.props.height
566
x_start = (gcompris.BOARD_WIDTH - len(self.items) * (iwidth + gap) ) / 2
567
y_start = (gcompris.BOARD_HEIGHT - len(self.items[0]) \
568
* (iheight + gap) ) / 2 - 40
571
parent = self.rootitem,
574
width = len(self.items) * (iwidth + gap) + gap,
575
height = len(self.items[0]) * (iheight + gap) + gap,
576
fill_color_rgba = 0x445533AAL,
577
stroke_color_rgba = 0xC0C0C0AAL,
583
data = self.data[self.gcomprisBoard.level - 1]
584
for y in range(len(self.items)):
585
for x in range(len(self.items[0])):
586
item = goocanvas.Rect(
587
parent = self.rootitem,
588
x = x_start + (iwidth + gap) * x - gap/2,
589
y = y_start + (iheight + gap) * y - gap/2,
590
width = iwidth + gap,
591
height = iheight + gap,
592
stroke_color_rgba = 0xC0C0C0FAL,
593
fill_color_rgba = 0x5050509AL,
598
self.hints[y][x] = item
599
item.props.visibility = goocanvas.ITEM_INVISIBLE
605
item = goocanvas.Svg(
606
parent = self.rootitem,
607
svg_handle = svghandle,
610
item.set_data("state", state)
611
item.translate(x_start + (iwidth + gap) * x,
612
y_start + (iheight + gap) * y)
613
item.connect("button_press_event", self.button_press_event, [y,x])
614
self.items[y][x] = item
618
def is_on(self, item):
619
return item.get_data("state")
621
def switch(self, item):
624
mystate = self.is_on(item)
626
item.set_properties(svg_id = "#on")
628
item.set_properties(svg_id = "#off")
629
item.set_data("state", not mystate)
631
def get_item_up(self, y, x):
634
return self.items[y-1][x]
636
def get_item_down(self, y, x):
637
if y == len(self.items[0])-1:
639
return self.items[y+1][x]
641
def get_item_left(self, y, x):
644
return self.items[y][x-1]
646
def get_item_right(self, y, x):
647
if x == len(self.items)-1:
649
return self.items[y][x+1]
651
# Returns True when complete
653
for y in range(len(self.items)):
654
for x in range(len(self.items[0])):
655
if self.is_on(self.items[y][x]):
660
def button_press_event(self, widget, target, event, spot):
662
self.switch(self.get_item_up(spot[0], spot[1]))
663
self.switch(self.get_item_left(spot[0], spot[1]))
664
self.switch(self.get_item_right(spot[0], spot[1]))
665
self.switch(self.get_item_down(spot[0], spot[1]))
670
self.tipiitem.props.visibility = goocanvas.ITEM_VISIBLE
671
self.textitem.props.visibility = goocanvas.ITEM_INVISIBLE
672
self.tuxitem.props.visibility = goocanvas.ITEM_INVISIBLE
673
self.bubbleitem.props.visibility = goocanvas.ITEM_INVISIBLE
675
gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER)
677
def solution_switch(self, items, clicks, y, x):
678
items[y][x] = not items[y][x]
679
clicks[y][x] = not clicks[y][x]
681
items[y-1][x] = not items[y-1][x]
683
items[y+1][x] = not items[y+1][x]
685
items[y][x-1] = not items[y][x-1]
687
items[y][x+1] = not items[y][x+1]
690
def chase_light(self, items, clicks):
691
for y in range(1, len(items)):
692
for x in range(len(items[0])):
694
self.solution_switch(items, clicks, y, x)
696
def is_solution_pattern(self, s, a, b, c, d, e):
697
if s[4][0] == a and \
706
# Return False if the is no solution
707
def solution_wrap(self, solution, clicks):
708
if self.is_solution_pattern(solution, 1, 0, 0 , 0, 1):
709
self.solution_switch(solution, clicks, 0, 0)
710
self.solution_switch(solution, clicks, 0, 1)
711
elif self.is_solution_pattern(solution, 0, 1, 0, 1, 0):
712
self.solution_switch(solution, clicks, 0, 0)
713
self.solution_switch(solution, clicks, 0, 3)
714
elif self.is_solution_pattern(solution, 1, 1, 1, 0, 0):
715
self.solution_switch(solution, clicks, 0, 1)
716
elif self.is_solution_pattern(solution, 0, 0, 1, 1 , 1):
717
self.solution_switch(solution, clicks, 0, 3)
718
elif self.is_solution_pattern(solution, 1, 0, 1, 1, 0):
719
self.solution_switch(solution, clicks, 0, 4)
720
elif self.is_solution_pattern(solution, 0, 1, 1, 0, 1):
721
self.solution_switch(solution, clicks, 0, 0)
722
elif self.is_solution_pattern(solution, 1, 1, 0, 1, 1):
723
self.solution_switch(solution, clicks, 0, 2)
728
def items2list(self, items):
730
for y in range(len(items[0])):
732
for x in range(len(items)):
733
if self.is_on(items[y][x]):
740
# We check only the last line
741
def solution_found(self, solution):
742
for x in range(len(solution[0])):
747
def solution_length(self, clicks):
749
for y in range(0, len(clicks)):
750
for x in range(len(clicks[0])):
755
def solve_one(self, solution, clicks):
757
for index in range(0, 5):
759
self.chase_light( solution, clicks )
761
if self.solution_found(solution):
765
if not self.solution_wrap(solution, clicks):
773
# Solving algorithm is the one described here:
774
# http://www.haar.clara.co.uk/Lights/solving.html To begin, you turn
775
# out all the lights on the top row, by pressing the buttons on the
776
# second row that are directly underneath any lit buttons on the top
777
# row. The top row will then have all it's lights off. Repeat this
778
# step for the second, third and fourth row. (i.e. chase the lights
779
# all the way down to the bottom row). This may have solved the
780
# puzzle already ( click here for an example of this ), but is more
781
# likely that there will now be some lights left on in the bottom
782
# row. If so, there are only 7 posible configurations. Depending on
783
# which configuration you are left with, you need to press some
784
# buttons in the top row. You can determine which buttons you need
785
# to press from the following table.
786
# Light on bottom row Press on this on top row
798
# Our solving algorithm does not find the shortest solution. We
799
# don't really care but we'd like to keep the proposed solution
800
# stable (not propose a complete new solution when one light
801
# changes). To achieve this (closely), we test here all the
802
# combination of the first line, trying to find the shortest
805
solution = self.items2list(self.items)
806
clicks2 = self.create_empty_list()
808
self.solution_switch(solution, clicks2, 0, 0)
810
self.solution_switch(solution, clicks2, 0, 1)
812
self.solution_switch(solution, clicks2, 0, 2)
814
self.solution_switch(solution, clicks2, 0, 3)
816
self.solution_switch(solution, clicks2, 0, 4)
818
clicks2 = self.solve_one(solution, clicks2)
819
if clicks == None and clicks2:
822
self.solution_length(clicks2) < self.solution_length(clicks):
826
self.show_hints(clicks)
827
self.update_background(clicks)
831
def solve_event(self, widget, target, event):
832
clicks = self.create_empty_list()
833
self.hints_mode = not self.hints_mode
834
if not self.hints_mode:
835
self.show_hints(clicks)
840
def update_background(self, clicks):
841
length = self.solution_length(clicks)
842
c = int(length * 0xFF / 18.0)
843
color = 0X33 << 24 | 0x11 << 16 | c << 8 | 0xFFL
844
self.background.set_properties(fill_color_rgba = color)
846
self.sunitem.translate(0, self.sunitem_offset)
847
self.sunitem_offset = length * 10
848
self.sunitem.translate(0, self.sunitem_offset * -1)
851
def show_hints(self, clicks):
852
for y in range(len(clicks)):
853
for x in range(len(clicks[0])):
855
self.hints[y][x].props.visibility = goocanvas.ITEM_VISIBLE
857
self.hints[y][x].props.visibility = goocanvas.ITEM_INVISIBLE
859
def print_sol(self, clicks):
860
for y in range(len(clicks)):
862
for x in range(len(clicks[0])):