1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00
PySolFC/pysollib/resource.py
skomoroh 5175caf86a + separated demo-mode and solver; solver dialog
+ added statistics progression; statistics progression dialog
+ new animation speed: `medium'; renamed `timer based' to `fast'
+ added flip animation (animatedFlip)
+ added move to waste animation (animatedFlipAndMove)
+ added cancel-drag animation (moveCardsBackHandler)
* improved demo game (snapshots based check for loop)
- removed setting text color from file name
- removed auto generation shadows (too slow)
* optimized menu creation
* changed some keybindings
* updated russian translation
* many bugfixes


git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@138 efabe8c0-fbe8-4139-b769-b5e6d273206e
2007-02-16 23:20:02 +00:00

532 lines
17 KiB
Python

## vim:ts=4:et:nowrap
##
##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
## All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING.
## If not, write to the Free Software Foundation, Inc.,
## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
## Markus F.X.J. Oberhumer
## <markus@oberhumer.com>
## http://www.oberhumer.com/pysol
##
##---------------------------------------------------------------------------##
# imports
import os, glob
# PySol imports
from mfxutil import Struct, KwStruct, EnvError
from settings import DEBUG
gettext = _
# /***********************************************************************
# // Abstract
# ************************************************************************/
class Resource(Struct):
def __init__(self, **kw):
kw = KwStruct(kw,
name = "",
filename = "",
basename = "", # basename of filename
absname = "", # absolute filename
# implicit
index = -1,
error = 0, # error while loading this resource
)
Struct.__init__(self, **kw.getKw())
def getSortKey(self):
return self.name.lower()
#return latin1_to_ascii(self.name).lower()
class ResourceManager:
def __init__(self):
self._selected_key = -1
self._objects = []
self._objects_by_name = None
self._objects_cache_name = {}
self._objects_cache_filename = {}
self._objects_cache_basename = {}
self._objects_cache_absname = {}
def getSelected(self):
return self._selected_key
def setSelected(self, index):
assert -1 <= index < len(self._objects)
self._selected_key = index
def len(self):
return len(self._objects)
def register(self, obj):
assert obj.index == -1
assert obj.name and obj.name not in self._objects_cache_name
self._objects_cache_name[obj.name] = obj
if obj.filename:
obj.absname = os.path.abspath(obj.filename)
obj.basename = os.path.basename(obj.filename)
self._objects_cache_filename[obj.filename] = obj
self._objects_cache_basename[obj.basename] = obj
self._objects_cache_absname[obj.absname] = obj
obj.index = len(self._objects)
self._objects.append(obj)
self._objects_by_name = None # invalidate
def get(self, index):
if 0 <= index < len(self._objects):
return self._objects[index]
return None
def getByName(self, key):
return self._objects_cache_name.get(key)
def getByBasename(self, key):
return self._objects_cache_basename.get(key)
def getAll(self):
return tuple(self._objects)
def getAllSortedByName(self):
if self._objects_by_name is None:
l = map(lambda obj: (obj.getSortKey(), obj), self._objects)
l.sort()
self._objects_by_name = tuple(map(lambda item: item[1], l))
return self._objects_by_name
#
# static methods
#
def _addDir(self, result, dir):
try:
if dir:
dir = os.path.normpath(dir)
if dir and os.path.isdir(dir) and dir not in result:
result.append(dir)
except EnvError, ex:
pass
def getSearchDirs(self, app, search, env=None):
if isinstance(search, str):
search = (search,)
result = []
if env:
for d in os.environ.get(env, "").split(os.pathsep):
self._addDir(result, d.strip())
for dir in (app.dataloader.dir, app.dn.maint, app.dn.config):
if not dir:
continue
dir = os.path.normpath(dir)
if not dir or not os.path.isdir(dir):
continue
for s in search:
try:
if s[-2:] == "-*":
d = os.path.normpath(os.path.join(dir, s[:-2]))
self._addDir(result, d)
globdirs = glob.glob(d + "-*")
globdirs.sort()
for d in globdirs:
self._addDir(result, d)
else:
self._addDir(result, os.path.join(dir, s))
except EnvError, ex:
pass
if DEBUG >= 6:
print "getSearchDirs", env, search, "->", result
return result
# /***********************************************************************
# // Cardset
# ************************************************************************/
# CardsetInfo constants
class CSI:
# cardset size
SIZE_TINY = 1
SIZE_SMALL = 2
SIZE_MEDIUM = 3
SIZE_LARGE = 4
SIZE_XLARGE = 5
# cardset types
TYPE_FRENCH = 1
TYPE_HANAFUDA = 2
TYPE_TAROCK = 3
TYPE_MAHJONGG = 4
TYPE_HEXADECK = 5
TYPE_MUGHAL_GANJIFA = 6
TYPE_NAVAGRAHA_GANJIFA = 7
TYPE_DASHAVATARA_GANJIFA = 8
TYPE_TRUMP_ONLY = 9
TYPE = {
1: _("French type (52 cards)"),
2: _("Hanafuda type (48 cards)"),
3: _("Tarock type (78 cards)"),
4: _("Mahjongg type (42 tiles)"),
5: _("Hex A Deck type (68 cards)"),
6: _("Mughal Ganjifa type (96 cards)"),
7: _("Navagraha Ganjifa type (108 cards)"),
8: _("Dashavatara Ganjifa type (120 cards)"),
9: _("Trumps only type (variable cards)"),
}
TYPE_NAME = {
1: _("French"),
2: _("Hanafuda"),
3: _("Tarock"),
4: _("Mahjongg"),
5: _("Hex A Deck"),
6: _("Mughal Ganjifa"),
7: _("Navagraha Ganjifa"),
8: _("Dashavatara Ganjifa"),
9: _("Trumps only"),
}
# cardset styles
STYLE = {
1: _("Adult"), #
2: _("Animals"), #
3: _("Anime"), #
4: _("Art"), #
5: _("Cartoons"), #
6: _("Children"), #
7: _("Classic look"), #
8: _("Collectors"), # scanned collectors cardsets
9: _("Computers"), #
10: _("Engines"), #
11: _("Fantasy"), #
30: _("Ganjifa"), #
12: _("Hanafuda"), #
29: _("Hex A Deck"), #
13: _("Holiday"), #
28: _("Mahjongg"), #
14: _("Movies"), #
31: _("Matrix"), #
15: _("Music"), #
16: _("Nature"), #
17: _("Operating Systems"), # e.g. cards with Linux logos
19: _("People"), # famous people
20: _("Places"), #
21: _("Plain"), #
22: _("Products"), #
18: _("Round cardsets"), #
23: _("Science Fiction"), #
24: _("Sports"), #
27: _("Tarock"), #
25: _("Vehicels"), #
26: _("Video Games"), #
}
# cardset nationality (suit and rank symbols)
NATIONALITY = {
1021: _("Australia"), #
1001: _("Austria"), #
1019: _("Belgium"), #
1010: _("Canada"), #
1011: _("China"), #
1012: _("Czech Republic"), #
1013: _("Denmark"), #
1003: _("England"), #
1004: _("France"), #
1006: _("Germany"), #
1014: _("Great Britain"), #
1015: _("Hungary"), #
1020: _("India"), #
1005: _("Italy"), #
1016: _("Japan"), #
1002: _("Netherlands"), #
1007: _("Russia"), #
1008: _("Spain"), #
1017: _("Sweden"), #
1009: _("Switzerland"), #
1018: _("USA"), #
}
# cardset creation date
DATE = {
10: "1000 - 1099",
11: "1100 - 1199",
12: "1200 - 1299",
13: "1300 - 1399",
14: "1400 - 1499",
15: "1500 - 1599",
16: "1600 - 1699",
17: "1700 - 1799",
18: "1800 - 1899",
19: "1900 - 1999",
20: "2000 - 2099",
21: "2100 - 2199",
22: "2200 - 2299",
}
class CardsetConfig(Struct):
# see config.txt and _readCardsetConfig()
def __init__(self):
Struct.__init__(self,
# line[0]
version = 1,
ext = ".gif",
type = CSI.TYPE_FRENCH,
ncards = -1,
styles = [],
year = 0,
# line[1]
ident = "",
name = "",
# line[2]
CARDW = 0,
CARDH = 0,
CARDD = 0,
# line[3]
CARD_XOFFSET = 0,
CARD_YOFFSET = 0,
SHADOW_XOFFSET = 0,
SHADOW_YOFFSET = 0,
# line[4]
backindex = 0,
# line[5]
backnames = (),
# other
CARD_DX = 0, # relative pos of real card image within Card
CARD_DY = 0,
)
class Cardset(Resource):
def __init__(self, **kw):
# start with all fields from CardsetConfig
config = CardsetConfig()
kw = KwStruct(config.__dict__, **kw)
# si is the SelectionInfo struct that will be queried by
# the "select cardset" dialogs. It can be freely modified.
si = Struct(type=0, size=0, styles=[], nationalities=[], dates=[])
kw = KwStruct(kw,
# essentials
ranks = (),
suits = (),
trumps = (),
nbottoms = 7,
nletters = 4,
nshadows = 1 + 13,
# selection criterias
si = si,
# implicit
backname = None,
dir = "",
)
Resource.__init__(self, **kw.getKw())
def getFaceCardNames(self):
names = []
for suit in self.suits:
for rank in self.ranks:
names.append("%02d%s" % (rank + 1, suit))
for trump in self.trumps:
names.append("%02d%s" % (trump + 1, "z"))
assert len(names) == self.ncards
return names
def getPreviewCardNames(self):
names = self.getFaceCardNames()
pnames = []
ranks, suits = self.ranks, self.suits
lr, ls = len(ranks), len(suits)
if lr == 0 or ls == 0: # TYPE_TRUMP_ONLY
return names[:16], 4
if lr >= 4:
ls = min(ls, 4)
low_ranks, high_ranks = 1, 3
###if self.type == 3: high_ranks = 4
for rank in range(0, low_ranks) + range(lr-high_ranks, lr):
for suit in range(ls):
index = suit * len(self.ranks) + rank
pnames.append(names[index % len(names)])
return pnames, ls
def updateCardback(self, backname=None, backindex=None):
# update default back
if isinstance(backname, str):
if backname in self.backnames:
backindex = self.backnames.index(backname)
if isinstance(backindex, int):
self.backindex = backindex % len(self.backnames)
self.backname = self.backnames[self.backindex]
class CardsetManager(ResourceManager):
def __init__(self):
ResourceManager.__init__(self)
self.registered_types = {}
self.registered_sizes = {}
self.registered_styles = {}
self.registered_nationalities = {}
self.registered_dates = {}
def _check(self, cs):
s = cs.type
if s not in CSI.TYPE:
return 0
cs.si.type = s
if s == CSI.TYPE_FRENCH:
cs.ranks = range(13)
cs.suits = "cshd"
elif s == CSI.TYPE_HANAFUDA:
cs.ranks = range(12)
cs.suits = "cshd"
elif s == CSI.TYPE_TAROCK:
cs.nbottoms = 8
cs.ranks = range(14)
cs.suits = "cshd"
cs.trumps = range(22)
elif s == CSI.TYPE_MAHJONGG:
cs.ranks = range(10)
cs.suits = "abc"
cs.trumps = range(12)
#
cs.nbottoms = 0
cs.nletters = 0
cs.nshadows = 0
elif s == CSI.TYPE_HEXADECK:
cs.nbottoms = 8
cs.ranks = range(16)
cs.suits = "cshd"
cs.trumps = range(4)
elif s == CSI.TYPE_MUGHAL_GANJIFA:
cs.nbottoms = 11
cs.ranks = range(12)
cs.suits = "abcdefgh"
elif s == CSI.TYPE_NAVAGRAHA_GANJIFA:
#???return 0 ## FIXME
cs.nbottoms = 12
cs.ranks = range(12)
cs.suits = "abcdefghi"
elif s == CSI.TYPE_DASHAVATARA_GANJIFA:
cs.nbottoms = 13
cs.ranks = range(12)
cs.suits = "abcdefghij"
elif s == CSI.TYPE_TRUMP_ONLY:
#???return 0 ## FIXME
#cs.nbottoms = 7
#cs.ranks = ()
#cs.suits = ""
#cs.trumps = range(cs.ncards)
cs.nbottoms = 1
cs.nletters = 0
cs.nshadows = 0
cs.ranks = ()
cs.suits = ""
cs.trumps = range(cs.ncards)
else:
return 0
return 1
def register(self, cs):
if not self._check(cs):
return
cs.ncards = len(cs.ranks) * len(cs.suits) + len(cs.trumps)
cs.name = cs.name[:25]
if not (1 <= cs.si.size <= 5):
CW, CH = cs.CARDW, cs.CARDH
if CW <= 55 and CH <= 72:
cs.si.size = CSI.SIZE_TINY
elif CW <= 60 and CH <= 85:
cs.si.size = CSI.SIZE_SMALL
elif CW <= 75 and CH <= 105:
cs.si.size = CSI.SIZE_MEDIUM
elif CW <= 90 and CH <= 125:
cs.si.size = CSI.SIZE_LARGE
else:
cs.si.size = CSI.SIZE_XLARGE
#
keys = cs.styles[:]
cs.si.styles = tuple(filter(lambda s: s in CSI.STYLE, keys))
for s in cs.si.styles:
self.registered_styles[s] = self.registered_styles.get(s, 0) + 1
cs.si.nationalities = tuple(filter(lambda s: s in CSI.NATIONALITY, keys))
for s in cs.si.nationalities:
self.registered_nationalities[s] = self.registered_nationalities.get(s, 0) + 1
keys = (cs.year / 100,)
cs.si.dates = tuple(filter(lambda s: s in CSI.DATE, keys))
for s in cs.si.dates:
self.registered_dates[s] = self.registered_dates.get(s, 0) + 1
#
s = cs.si.type
self.registered_types[s] = self.registered_types.get(s, 0) + 1
s = cs.si.size
self.registered_sizes[s] = self.registered_sizes.get(s, 0) + 1
cs.updateCardback()
ResourceManager.register(self, cs)
# /***********************************************************************
# // Tile
# ************************************************************************/
class Tile(Resource):
def __init__(self, **kw):
kw['color'] = None
kw['stretch'] = 0
Resource.__init__(self, **kw)
class TileManager(ResourceManager):
pass
# /***********************************************************************
# // Sample
# ************************************************************************/
class Sample(Resource):
def __init__(self, **kw):
kw['volume'] = -1
Resource.__init__(self, **kw)
class SampleManager(ResourceManager):
pass
# /***********************************************************************
# // Music
# ************************************************************************/
class Music(Sample):
pass
class MusicManager(SampleManager):
pass