mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-15 02:54:09 -04:00
Compare commits
37 commits
2c16d5773d
...
513726360d
Author | SHA1 | Date | |
---|---|---|---|
|
513726360d | ||
|
52d37cfeae | ||
|
c7a3907d25 | ||
|
8244d19e6e | ||
|
70123aaa5d | ||
|
9666c56a9f | ||
|
6f591dd519 | ||
|
bce3354f3d | ||
|
abc9c2c3aa | ||
|
90d36213e8 | ||
|
b90b3ed1e1 | ||
|
8dbd5c552f | ||
|
bf620c6310 | ||
|
cc9adb6813 | ||
|
18d3b2713a | ||
|
0a220861a9 | ||
|
42ede36daf | ||
|
451e5a5003 | ||
|
928c8a2819 | ||
|
89a6728bf8 | ||
|
55edd37a72 | ||
|
c6d093d995 | ||
|
b92091d1c3 | ||
|
b26786a877 | ||
|
4b7df34d67 | ||
|
a004f7e690 | ||
|
ccacdbe148 | ||
|
9169297801 | ||
|
586cd4a05f | ||
|
ce72838aff | ||
|
3649a52ab2 | ||
|
3f660b25a0 | ||
|
ca93af4897 | ||
|
2fea665242 | ||
|
d0ff5494d5 | ||
|
3793d8392e | ||
|
0fd5e40b54 |
52 changed files with 214 additions and 2251 deletions
|
@ -37,11 +37,16 @@ test_script:
|
|||
- appveyor DownloadFile https://sourceforge.net/projects/pysolfc/files/PySol-Music/PySol-Music-4.50/pysol-music-4.50.tar.xz/download -FileName music.tar.xz
|
||||
- 7z x music.tar.xz -so | 7z x -si -ttar
|
||||
- move pysol-music-4.50\data\music dist\data\music
|
||||
- ps: |
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$project = Invoke-RestMethod -Method Get -Uri "$apiUrl/projects/shlomif/fc-solve"
|
||||
$jobId = $project.build.jobs[0].jobId
|
||||
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/fc-solve/pkg-build/fc-solve-for-pysol.zip" -OutFile fc-solve.zip
|
||||
# - ps: |
|
||||
# $apiUrl = 'https://ci.appveyor.com/api'
|
||||
# $project = Invoke-RestMethod -Method Get -Uri "$apiUrl/projects/shlomif/fc-solve"
|
||||
# $jobId = $project.build.jobs[0].jobId
|
||||
# Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/fc-solve/pkg-build/fc-solve-for-pysol.zip" -OutFile fc-solve.zip
|
||||
#
|
||||
# For reproducible builds:
|
||||
# See: https://reproducible-builds.org/ .
|
||||
- SET FC_SOLVE_VER=5.20.0
|
||||
- appveyor DownloadFile https://netix.dl.sourceforge.net/project/fc-solve/fc-solve/DO-NOT-USE--fc-solve-for-pysol/fc-solve-for-pysol--v%FC_SOLVE_VER%.zip -FileName fc-solve.zip
|
||||
- 7z x fc-solve.zip
|
||||
- move fcs-pysol dist\freecell-solver
|
||||
- 7z a -r pysol_win_dist.7z dist\
|
||||
|
|
21
.travis.yml
21
.travis.yml
|
@ -1,7 +1,7 @@
|
|||
addons:
|
||||
homebrew:
|
||||
update: true
|
||||
brewfile: true
|
||||
update: true
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/perl_modules
|
||||
|
@ -18,12 +18,25 @@ deploy:
|
|||
skip_cleanup: true
|
||||
dist: bionic
|
||||
before_install:
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then wget --content-disposition https://sourceforge.net/projects/pysolfc/files/PySolFC-Cardsets/minimal/PySolFC-Cardsets--Minimal-2.0.tar.xz/download && tar xJf PySolFC-Cardsets--Minimal-2.0.tar.xz && mv PySolFC-Cardsets--Minimal-2.0/cardset-* data ; fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then
|
||||
wget --content-disposition https://sourceforge.net/projects/pysolfc/files/PySolFC-Cardsets/minimal/PySolFC-Cardsets--Minimal-2.0.tar.xz/download &&
|
||||
tar xJf PySolFC-Cardsets--Minimal-2.0.tar.xz &&
|
||||
mv PySolFC-Cardsets--Minimal-2.0/cardset-* data ;
|
||||
fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then
|
||||
PYVER=3.8.2 &&
|
||||
PYVER_SHORT=3.8 &&
|
||||
wget -O python.pkg "https://www.python.org/ftp/python/${PYVER}/python-${PYVER}-macosx10.9.pkg" &&
|
||||
sudo installer -pkg python.pkg -target / &&
|
||||
export PATH="/Library/Frameworks/Python.framework/Versions/${PYVER_SHORT}/bin:${PATH}" ;
|
||||
fi
|
||||
install:
|
||||
# Tests are failing for them sometimes
|
||||
- sudo cpanm --notest Capture::Tiny IPC::System::Simple
|
||||
- sudo cpanm Code::TidyAll::Plugin::Flake8 Perl::Tidy Test::Code::TidyAll Test::Differences Test::TrailingSpace
|
||||
|
||||
- export PY_MODS='attrs configobj pycotap pysol-cards random2 setuptools six'
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then export PY_MODS="--no-binary=Pillow Pillow $PY_MODS" ; fi
|
||||
- sudo pip3 install --upgrade $PY_MODS flake8 flake8-import-order
|
||||
- sudo pip2 install --upgrade $PY_MODS
|
||||
language: generic
|
||||
|
@ -47,6 +60,6 @@ matrix:
|
|||
script:
|
||||
- export TIDYALL_DATA_DIR="$HOME/tidyall_d"
|
||||
- bash -x scripts/travis-ci-build
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then PYTHONPATH="`pwd`" python3 setup_osx.py py2app ; fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then PYTHONPATH="." python3 setup_osx.py py2app ; fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then find . -iname '*.app' ; fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then (cd dist; tar -cvf ../PySolFC.app.tar PySolFC.app/ ; ); xz PySolFC.app.tar ; fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then (cd dist && tar -cvf ../PySolFC.app.tar PySolFC.app/) && xz PySolFC.app.tar ; fi
|
||||
|
|
3
Brewfile
3
Brewfile
|
@ -2,8 +2,7 @@ brew "perl"
|
|||
brew "cpanminus"
|
||||
brew "gettext", link: true
|
||||
brew "gnutls"
|
||||
brew "jpeg"
|
||||
brew "openssl"
|
||||
brew "tcl-tk"
|
||||
brew "python@2"
|
||||
brew "python"
|
||||
brew "pyenv-virtualenv"
|
||||
|
|
|
@ -46,7 +46,7 @@ graft html-src
|
|||
#graft data/images
|
||||
recursive-include data/images *.gif *.png *.jpg
|
||||
graft data/tiles
|
||||
include data/pysol.xbm data/pysol.xpm data/pysol.ico data/PySol.icns
|
||||
include data/pysol.ico data/PySol.icns
|
||||
##
|
||||
## data - sound
|
||||
##
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
[[news]]
|
||||
=== News
|
||||
|
||||
* _5 March, 2020:_ There is a new stable release
|
||||
https://sourceforge.net/projects/pysolfc/files/PySolFC/PySolFC-2.8.0/[PySolFC
|
||||
v2.8.0]. New in this release:
|
||||
** Better kivy/Android support
|
||||
** Using ttk and configobj as shipped in the python dist (instead of forked versions)
|
||||
** Requiring https://pypi.org/project/attrs/[attrs] and https://pypi.org/project/pysol-cards/[pysol-cards] from PyPI
|
||||
** Added tests, bug fixes and refactorings.
|
||||
** Add the +-g+ and +--deal+ command line options.
|
||||
* _25 April, 2019:_ There is a new stable release
|
||||
https://sourceforge.net/projects/pysolfc/files/PySolFC/PySolFC-2.6.4/[PySolFC
|
||||
v2.6.4]. New in this release:
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
|
||||
Prerequisites (needs root):
|
||||
|
||||
(For updated information on these subjects please consult the scripts in
|
||||
android/debian).
|
||||
|
||||
On a 'freshly installed' Ubuntu 16.04 (32bit), the following
|
||||
additional packages had to be installed:
|
||||
|
||||
|
|
|
@ -18,3 +18,17 @@ apt-get install -y \
|
|||
lld
|
||||
|
||||
update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
|
||||
|
||||
apt-get install -y --no-install-recommends \
|
||||
imagemagick \
|
||||
python3-tk \
|
||||
ccache \
|
||||
libltdl-dev
|
||||
|
||||
# set python3 as default.
|
||||
# make python alternatives selectable.
|
||||
# (debian stretch, adapt for different versions)
|
||||
|
||||
update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1
|
||||
update-alternatives --install /usr/bin/python python /usr/bin/python3.5 2
|
||||
|
||||
|
|
|
@ -5,22 +5,27 @@ set -eux
|
|||
|
||||
. mkp4a.common
|
||||
|
||||
if [[ -d $sdkdir && -d $ndkdir ]]; then
|
||||
echo "Skipping SDK and NDK installation: SDK and NDK directories already exist."
|
||||
exit
|
||||
urlbase=https://dl.google.com/android/repository/
|
||||
|
||||
if [[ -d $ndkdir ]]; then
|
||||
echo "Skipping NDK installation: NDK directory already exists."
|
||||
else
|
||||
mkdir -p $ndkdir
|
||||
ndk_zip=android-ndk-${ndkver}-linux-x86_64.zip
|
||||
[ -a $ndk_zip ] || wget $urlbase/$ndk_zip
|
||||
unzip -d $(dirname $ndkdir) $ndk_zip
|
||||
fi
|
||||
|
||||
urlbase=https://dl.google.com/android/repository/
|
||||
tools_zip=sdk-tools-linux-4333796.zip
|
||||
ndk_zip=android-ndk-r17c-linux-x86_64.zip
|
||||
if [[ -d $sdkdir ]]; then
|
||||
echo "Skipping SDK installation: SDK directory already exists."
|
||||
else
|
||||
mkdir -p $sdkdir
|
||||
tools_zip=sdk-tools-linux-4333796.zip
|
||||
[ -a $tools_zip ] || wget $urlbase/$tools_zip
|
||||
unzip -d $sdkdir $tools_zip
|
||||
|
||||
mkdir -p $sdkdir $ndkdir
|
||||
$sdkdir/tools/bin/sdkmanager 'platforms;android-27'
|
||||
$sdkdir/tools/bin/sdkmanager 'build-tools;29.0.1'
|
||||
fi
|
||||
|
||||
[ -a $ndk_zip ] || wget $urlbase/$ndk_zip
|
||||
unzip -d $(dirname $ndkdir) $ndk_zip
|
||||
|
||||
[ -a $tools_zip ] || wget $urlbase/$tools_zip
|
||||
unzip -d $sdkdir $tools_zip
|
||||
|
||||
$sdkdir/tools/bin/sdkmanager 'platforms;android-27'
|
||||
$sdkdir/tools/bin/sdkmanager 'build-tools;29.0.1'
|
||||
|
|
|
@ -8,8 +8,11 @@ tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
|||
cardsets_dir='PySolFC-Cardsets--Minimal-2.0.1'
|
||||
cardsets_file="${cardsets_dir}.tar.xz"
|
||||
|
||||
p4aver="2019.10.6"
|
||||
ndkver="r19c"
|
||||
|
||||
sdkdir="${HOME}/.cache/sdk-for-p4a/sdk"
|
||||
ndkdir="${HOME}/.cache/sdk-for-p4a/android-ndk-r17c"
|
||||
ndkdir="${HOME}/.cache/sdk-for-p4a/android-ndk-${ndkver}"
|
||||
pkgdir="${HOME}/.local/share/python-for-android/packages"
|
||||
|
||||
# gradle may need this.
|
||||
|
@ -27,7 +30,7 @@ p4a_options="\
|
|||
--private ${tmpdir} \
|
||||
--orientation sensor \
|
||||
--icon ${tmpdir}/data/images/icons/48x48/pysol.png \
|
||||
--presplash ${tmpdir}/data/images/icons/1024x1024/pysol.png \
|
||||
--presplash ${tmpdir}/data/images/logo-with-margin-1024.png \
|
||||
--copy-libs \
|
||||
--add-jar ${tmpdir}/support-v4-24.1.1.aar \
|
||||
--color always"
|
||||
|
|
|
@ -17,7 +17,16 @@ echo '### install p4a'
|
|||
|
||||
if [[ $# == 0 ]]
|
||||
then
|
||||
python3 -m pip install -q --user python-for-android
|
||||
if not python3 -m pip install -q --user "python-for-android==${p4aver}"
|
||||
then
|
||||
echo "obviously inside a virtualenv, so omit --user"
|
||||
if python3 -m pip install -q "python-for-android==${p4aver}"
|
||||
then
|
||||
echo "done"
|
||||
fi
|
||||
else
|
||||
echo "done"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo '### prepare source'
|
||||
|
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
@ -1,20 +0,0 @@
|
|||
#define pysol_width 41
|
||||
#define pysol_height 40
|
||||
static char pysol_bits[] = {
|
||||
0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x7c,
|
||||
0x00,0x00,0xfe,0x00,0x00,0xff,0x01,0x00,0xfe,0x00,0x80,0xff,0x03,0x00,0xfe,
|
||||
0x00,0xc0,0xff,0x07,0x00,0xfe,0x00,0xe0,0xff,0x0f,0x00,0xfe,0x00,0xe0,0xff,
|
||||
0x0f,0x00,0xfe,0x00,0xf0,0xff,0x1f,0x00,0xfe,0x00,0xf0,0xff,0x1f,0x00,0xfe,
|
||||
0x00,0xf0,0xff,0x1f,0x00,0xfe,0x00,0xf0,0xff,0x1f,0x00,0xfe,0x00,0xf0,0xff,
|
||||
0x1f,0x00,0xfe,0x00,0xe0,0xff,0x0f,0x00,0xfe,0x00,0xe0,0xff,0x0f,0x00,0xfe,
|
||||
0x00,0xc0,0xff,0x07,0x00,0xfe,0x00,0x9f,0xff,0xf3,0x01,0xfe,0xc0,0x7f,0xff,
|
||||
0xfd,0x07,0xfe,0xe0,0xff,0x7c,0xfe,0x0f,0xfe,0xf0,0xff,0x39,0xff,0x1f,0xfe,
|
||||
0xf8,0xff,0xbb,0xff,0x3f,0xfe,0xf8,0xff,0xbb,0xff,0x3f,0xfe,0xfc,0xff,0xff,
|
||||
0xff,0x7f,0xfe,0xfc,0xff,0xff,0xff,0x7f,0xfe,0xfc,0xff,0xff,0xff,0x7f,0xfe,
|
||||
0xfc,0xff,0xff,0xff,0x7f,0xfe,0xfc,0xff,0xff,0xff,0x7f,0xfe,0xf8,0xff,0xbb,
|
||||
0xff,0x3f,0xfe,0xf8,0xff,0xbb,0xff,0x3f,0xfe,0xf0,0xff,0x39,0xff,0x1f,0xfe,
|
||||
0xe0,0xff,0x39,0xfe,0x0f,0xfe,0xc0,0x7f,0x38,0xfc,0x07,0xfe,0x00,0x1f,0x7c,
|
||||
0xf0,0x01,0xfe,0x00,0x00,0x7c,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,
|
||||
0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xff,0x01,0x00,0xfe,0x00,0x80,0xff,
|
||||
0x03,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0xfe
|
||||
};
|
|
@ -1,49 +0,0 @@
|
|||
/* XPM */
|
||||
static char *magick[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"32 32 11 1",
|
||||
" c Gray0",
|
||||
". c #191919",
|
||||
"X c Gray20",
|
||||
"o c #4c4c4c",
|
||||
"O c #666667",
|
||||
"+ c Gray60",
|
||||
"@ c #b2b2b2",
|
||||
"# c Gray80",
|
||||
"$ c Gray90",
|
||||
"% c Gray100",
|
||||
"& c None",
|
||||
/* pixels */
|
||||
"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&&&&+@@@&&&&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&&@o o@&&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&# #&&&&&&&&&&&",
|
||||
"&&&&&&&&&&# #&&&&&&&&&&",
|
||||
"&&&&&&&&&& &&&&&&&&&&",
|
||||
"&&&&&&&&&+ @&&&&&&&&&",
|
||||
"&&&&&&&&&o o&&&&&&&&&",
|
||||
"&&&&&&&&&o o&&&&&&&&&",
|
||||
"&&&&&&&&&o o&&&&&&&&&",
|
||||
"&&&&&&&&&# #&&&&&&&&&",
|
||||
"&&&&&&&&&&. .&&&&&&&&&&",
|
||||
"&&&&&&$$$$&X X&$$$$&&&&&&",
|
||||
"&&&&$@ +@X X@+ @$&&&&",
|
||||
"&&&&X .+@ @@ X&&&&",
|
||||
"&&&X .&$ $& X&&&",
|
||||
"&&o $ $ o&&",
|
||||
"&&. + + .$&",
|
||||
"&+ +&",
|
||||
"&+ +&",
|
||||
"&+ +&",
|
||||
"&# O O #&",
|
||||
"&&o $ $ o&&",
|
||||
"&&$ @& &@ .$&&",
|
||||
"&&&& .$& &&+ &&&&",
|
||||
"&&&&$O o+&&+ +&&+o O$&&&&",
|
||||
"&&&&&&@+@+&&&& &&&&+@+@&&&&&&",
|
||||
"&&&&&&&&&&&&&O O&&&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&&&. .&&&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&$. .$&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&$+++@@@++$&&&&&&&&&&&",
|
||||
"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
|
||||
};
|
|
@ -183,7 +183,7 @@ class Application:
|
|||
try:
|
||||
approc = self.mainproc() # setup process
|
||||
approc.send(None) # and go
|
||||
except Exception:
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
def gameproc(self):
|
||||
|
|
|
@ -78,6 +78,11 @@ if TOOLKIT == 'tk':
|
|||
else:
|
||||
from pysollib.pysoltk import reset_solver_dialog
|
||||
|
||||
# See: https://github.com/shlomif/PySolFC/issues/159 .
|
||||
# 'factory=' is absent from older versions.
|
||||
assert getattr(attr, '__version_info__', (0, 0, 0)) >= (18, 2, 0), (
|
||||
"Newer version of https://pypi.org/project/attrs/ is required.")
|
||||
|
||||
|
||||
PLAY_TIME_TIMEOUT = 200
|
||||
S_PLAY = 0x40
|
||||
|
|
|
@ -82,8 +82,10 @@ class Pyramid_StackMethods:
|
|||
def _dropPairMove(self, n, other_stack, frames=-1, shadow=-1):
|
||||
if not self.game.demo:
|
||||
self.game.playSample("droppair", priority=200)
|
||||
assert n == 1 and self.acceptsCards(
|
||||
other_stack, [other_stack.cards[-1]])
|
||||
if not (n == 1
|
||||
and other_stack.cards
|
||||
and self.acceptsCards(other_stack, [other_stack.cards[-1]])):
|
||||
return
|
||||
old_state = self.game.enterState(self.game.S_FILL)
|
||||
f = self.game.s.foundations[0]
|
||||
self.game.moveMove(n, self, f, frames=frames, shadow=shadow)
|
||||
|
|
|
@ -31,6 +31,7 @@ from kivy.base import EventLoop
|
|||
from kivy.base import stopTouchApp
|
||||
from kivy.cache import Cache
|
||||
from kivy.clock import Clock
|
||||
from kivy.config import Config
|
||||
from kivy.core.audio import SoundLoader
|
||||
from kivy.core.window import Window
|
||||
from kivy.graphics import Color
|
||||
|
@ -54,6 +55,9 @@ from kivy.utils import platform
|
|||
|
||||
from pysollib.kivy.androidperms import requestStoragePerm
|
||||
|
||||
if platform != 'android':
|
||||
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
|
||||
|
||||
# =============================================================================
|
||||
|
||||
|
||||
|
@ -713,13 +717,27 @@ class LImageItem(BoxLayout, LBase):
|
|||
# ev. noch globales cache für stacks->game und cards->stack
|
||||
# einrichten. Aber: stacks hängt vom jeweiligen spiel ab.
|
||||
|
||||
def send_event_pressed_1(self, event):
|
||||
if self.group and '<1>' in self.group.bindings:
|
||||
self.group.bindings['<1>'](event)
|
||||
def send_event_pressed_n(self, event, n):
|
||||
if self.group and n in self.group.bindings:
|
||||
self.group.bindings[n](event)
|
||||
|
||||
def send_event_pressed_double_1(self, event):
|
||||
if self.group and '<Double-1>' in self.group.bindings:
|
||||
self.group.bindings['<Double-1>'](event)
|
||||
def send_event_pressed(self, touch, event):
|
||||
|
||||
if touch.is_double_tap:
|
||||
self.send_event_pressed_n(event, '<Double-1>')
|
||||
else:
|
||||
button = 'left'
|
||||
if 'button' in touch.profile:
|
||||
button = touch.button
|
||||
if button == 'left':
|
||||
self.send_event_pressed_n(event, '<1>')
|
||||
return
|
||||
if button == 'middle':
|
||||
self.send_event_pressed_n(event, '<2>')
|
||||
return
|
||||
if button == 'right':
|
||||
self.send_event_pressed_n(event, '<3>')
|
||||
return
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
|
||||
|
@ -743,10 +761,7 @@ class LImageItem(BoxLayout, LBase):
|
|||
event.y = ppos[1]
|
||||
self.dragstart = touch.pos
|
||||
event.cardid = i
|
||||
if touch.is_double_tap:
|
||||
self.send_event_pressed_double_1(event)
|
||||
else:
|
||||
self.send_event_pressed_1(event)
|
||||
self.send_event_pressed(touch, event)
|
||||
return True
|
||||
|
||||
if self.group is not None:
|
||||
|
@ -1777,10 +1792,9 @@ class LApp(App):
|
|||
|
||||
def doSize(self, obj, val):
|
||||
mval = self.mainWindow.size
|
||||
if (val[0] != mval[0] and val[1] != mval[1]):
|
||||
logging.info("LApp: size changed %s - %s (%s)" % (obj, val, mval))
|
||||
# Clock.schedule_once(self.delayedRebuild, 0.01)
|
||||
Clock.schedule_once(self.makeDelayedRebuild(), 0.01)
|
||||
# self.mainWindow.rebuildContainer()
|
||||
pass
|
||||
|
||||
def on_start(self):
|
||||
|
|
|
@ -66,3 +66,9 @@ class KpatXmlEmitter:
|
|||
|
||||
def writeInitialLayout(self, state, turn_cb):
|
||||
assert False # unimpl
|
||||
|
||||
|
||||
class KpatEmitter:
|
||||
"""docstring for KpatEmitter"""
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
|
|
|
@ -93,7 +93,7 @@ def parse_option(argv):
|
|||
"sound-mod=",
|
||||
"help"])
|
||||
except getopt.GetoptError as err:
|
||||
print_err(err + "\n" + _("try %s --help for more information") %
|
||||
print_err(str(err) + "\n" + _("try %s --help for more information") %
|
||||
prog_name, 0)
|
||||
return None
|
||||
opts = {"help": False,
|
||||
|
@ -355,7 +355,10 @@ Please check your %(app)s installation.
|
|||
if music:
|
||||
app.music_playlist = list(music)[:]
|
||||
app.miscrandom.shuffle(app.music_playlist)
|
||||
if 1:
|
||||
# Cancelling because otherwise people complain
|
||||
# that the music order is not random.
|
||||
SHOULD_PUT_bye_for_now_FIRST = False
|
||||
if SHOULD_PUT_bye_for_now_FIRST:
|
||||
for m in app.music_playlist:
|
||||
if m.name.lower() == "bye_for_now":
|
||||
app.music_playlist.remove(m)
|
||||
|
|
|
@ -87,14 +87,7 @@ class PysolProgressBar:
|
|||
im.show()
|
||||
im.set_property('xpad', 10)
|
||||
im.set_property('ypad', 5)
|
||||
# set icon
|
||||
# if app:
|
||||
# try:
|
||||
# name = app.dataloader.findFile('pysol.xpm')
|
||||
# bg = self.top.get_style().bg[gtk.STATE_NORMAL]
|
||||
# pixmap, mask = create_pixmap_from_xpm(self.top, bg, name)
|
||||
# self.top.set_icon(pixmap, mask)
|
||||
# except: pass
|
||||
|
||||
setTransient(self.top, parent)
|
||||
self.top.show()
|
||||
self.top.window.set_cursor(gdk.Cursor(gdk.WATCH))
|
||||
|
|
|
@ -393,7 +393,6 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
audio=self.app.audio,
|
||||
canvas=canvas,
|
||||
cardset=self.app.cardset.copy(),
|
||||
comments=self.app.comments.new(),
|
||||
gamerandom=self.app.gamerandom,
|
||||
gdb=self.app.gdb,
|
||||
gimages=self.app.gimages,
|
||||
|
|
|
@ -165,12 +165,7 @@ class _MfxToplevel(gtk.Window):
|
|||
pass
|
||||
|
||||
def wm_iconbitmap(self, name):
|
||||
print('wm_iconbitmap:', name)
|
||||
if name and name[0] == '@' and name[-4:] == '.xbm':
|
||||
name = name[1:-4] + '.xpm'
|
||||
# bg = self.get_style().bg[gtk.STATE_NORMAL]
|
||||
# pixmap, mask = create_pixmap_from_xpm(self, bg, name)
|
||||
# self.set_icon(pixmap, mask)
|
||||
pass
|
||||
|
||||
def wm_iconname(self, name):
|
||||
pass
|
||||
|
|
|
@ -32,7 +32,7 @@ PACKAGE = 'PySolFC'
|
|||
TITLE = 'PySol'
|
||||
PACKAGE_URL = 'http://pysolfc.sourceforge.net/'
|
||||
|
||||
VERSION_TUPLE = (2, 6, 4)
|
||||
VERSION_TUPLE = (2, 8, 0)
|
||||
VERSION = '.'.join(map(str, VERSION_TUPLE))
|
||||
|
||||
# Tk windowing system (auto set up in init.py)
|
||||
|
|
|
@ -2935,9 +2935,10 @@ class FaceUpWasteTalonStack(WasteTalonStack):
|
|||
self.game.fillStack(self)
|
||||
|
||||
def dealCards(self, sound=False):
|
||||
WasteTalonStack.dealCards(self, sound=sound)
|
||||
retval = WasteTalonStack.dealCards(self, sound=sound)
|
||||
if self.canFlipCard():
|
||||
self.flipMove()
|
||||
return retval
|
||||
|
||||
|
||||
class OpenTalonStack(TalonStack, OpenStack):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from . import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from .tkwidget import MfxDialog
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ from pysollib.ui.tktile.tkutil import bind
|
|||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_font
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog
|
||||
from .tkwidget import PysolScale
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ from pysollib.ui.tktile.menubar import MfxMenu, PysolMenubarTkCommon
|
|||
from pysollib.ui.tktile.solverdialog import connect_game_solver_dialog
|
||||
from pysollib.util import CARDSET
|
||||
|
||||
from . import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from .selectgame import SelectGameDialog, SelectGameDialogWithPreview
|
||||
from .selecttile import SelectTileDialogWithPreview
|
||||
from .soundoptionsdialog import SoundOptionsDialog
|
||||
|
|
|
@ -25,8 +25,8 @@ from pysollib.mfxutil import KwStruct
|
|||
from pysollib.mygettext import _
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog
|
||||
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ from pysollib.ui.tktile.tkconst import EVENT_HANDLED
|
|||
from pysollib.ui.tktile.tkutil import makeToplevel, setTransient
|
||||
|
||||
from six.moves import tkinter
|
||||
|
||||
from . import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
# ************************************************************************
|
||||
# * a simple progress bar
|
||||
|
|
|
@ -32,8 +32,8 @@ from pysollib.ui.tktile.tkutil import loadImage
|
|||
from pysollib.util import CARDSET
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .selecttree import SelectDialogTreeCanvas
|
||||
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
||||
from .tkwidget import MfxDialog, MfxScrolledCanvas, PysolScale
|
||||
|
|
|
@ -32,8 +32,8 @@ from pysollib.ui.tktile.selecttree import SelectDialogTreeData
|
|||
from pysollib.ui.tktile.tkutil import unbind_destroy
|
||||
|
||||
from six.moves import UserList
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .selecttree import SelectDialogTreeCanvas
|
||||
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
||||
from .tkwidget import MfxDialog, MfxScrolledCanvas
|
||||
|
@ -319,13 +319,14 @@ class SelectGameDialog(MfxDialog):
|
|||
if button == 0: # Ok or double click
|
||||
self.gameid = self.tree.selection_key
|
||||
self.tree.n_expansions = 1 # save xyview in any case
|
||||
if button == 1: # Rules
|
||||
if button == 10: # Rules
|
||||
doc = self.app.getGameRulesFilename(self.tree.selection_key)
|
||||
if not doc:
|
||||
return
|
||||
dir = os.path.join("html", "rules")
|
||||
from pysollib.help import help_html
|
||||
help_html(self.app, doc, dir, self.top)
|
||||
self.top.grab_release() # Don't want the help window appear frozen
|
||||
return
|
||||
MfxDialog.mDone(self, button)
|
||||
|
||||
|
@ -483,7 +484,6 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
audio=self.app.audio,
|
||||
canvas=canvas,
|
||||
cardset=self.app.cardset.copy(),
|
||||
comments=self.app.comments.new(),
|
||||
gamerandom=self.app.gamerandom,
|
||||
gdb=self.app.gdb,
|
||||
gimages=self.app.gimages,
|
||||
|
|
|
@ -28,8 +28,8 @@ from pysollib.ui.tktile.selecttree import SelectDialogTreeData
|
|||
import six
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_colorchooser
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .selecttree import SelectDialogTreeCanvas
|
||||
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
||||
from .tkwidget import MfxDialog, MfxScrolledCanvas
|
||||
|
|
|
@ -30,8 +30,8 @@ from pysollib.settings import TITLE
|
|||
from pysollib.ui.tktile.tkconst import EVENT_HANDLED
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog, MfxMessageDialog
|
||||
from .tkwidget import PysolScale
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ from pysollib.mygettext import _
|
|||
|
||||
import six
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxTooltip
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -25,8 +25,8 @@ from pysollib.mfxutil import KwStruct
|
|||
from pysollib.mygettext import _
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog, PysolScale
|
||||
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ from pysollib.mygettext import _
|
|||
from pysollib.ui.tktile.tkhtml import Base_HTMLViewer
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .statusbar import HtmlStatusbar
|
||||
from .tkwidget import MfxMessageDialog
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ from pysollib.ui.tktile.tkutil import bind, loadImage
|
|||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_font
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog, MfxMessageDialog
|
||||
|
||||
|
||||
|
|
|
@ -38,8 +38,7 @@ import six
|
|||
from six import PY2
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_font
|
||||
|
||||
from . import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
# ************************************************************************
|
||||
# * abstract base class for the dialogs in this module
|
||||
|
|
|
@ -33,8 +33,8 @@ from pysollib.util import IMAGE_EXTENSIONS
|
|||
from pysollib.winsystems import TkSettings
|
||||
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxTooltip
|
||||
|
||||
|
||||
|
|
1555
pysollib/tile/ttk.py
1555
pysollib/tile/ttk.py
File diff suppressed because it is too large
Load diff
|
@ -28,8 +28,8 @@ from pysollib.wizardutil import WizardWidgets
|
|||
|
||||
import six
|
||||
from six.moves import tkinter
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
from . import ttk
|
||||
from .tkwidget import MfxDialog
|
||||
from .tkwidget import PysolCombo, PysolScale
|
||||
|
||||
|
|
|
@ -484,7 +484,6 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
audio=self.app.audio,
|
||||
canvas=canvas,
|
||||
cardset=self.app.cardset.copy(),
|
||||
comments=self.app.comments.new(),
|
||||
gamerandom=self.app.gamerandom,
|
||||
gdb=self.app.gdb,
|
||||
gimages=self.app.gimages,
|
||||
|
|
|
@ -156,7 +156,7 @@ class PysolStatusbar(MfxStatusbar):
|
|||
self._createLabel(n, tooltip=t, width=w)
|
||||
#
|
||||
label = self._createLabel('info', expand=True)
|
||||
label.config(padding=(8, 0))
|
||||
label.config(padx=8)
|
||||
|
||||
|
||||
class HelpStatusbar(MfxStatusbar):
|
||||
|
|
|
@ -110,7 +110,7 @@ def makeToplevel(parent, title=None):
|
|||
def make_help_toplevel(app, title=None):
|
||||
# Create an independent Toplevel window.
|
||||
from pysollib.winsystems import init_root_window
|
||||
window = tkinter.Tk(className=TITLE)
|
||||
window = tkinter.Toplevel(class_=TITLE)
|
||||
init_root_window(window, app)
|
||||
return window
|
||||
|
||||
|
|
|
@ -161,11 +161,22 @@ class DataLoader:
|
|||
raise OSError("DataLoader could not find image "+filename +
|
||||
" in "+self.dir+" "+str(subdirs))
|
||||
|
||||
def findIcon(self, filename='pysol', subdirs=None):
|
||||
root, ext = os.path.splitext(filename)
|
||||
if not ext:
|
||||
filename += ('.ico' if os.name == 'nt' else '.xbm')
|
||||
return self.findFile(filename, subdirs)
|
||||
def findAllIconSizes(self, filename='pysol.png'):
|
||||
try:
|
||||
icondir = self.findDir(os.path.join('images', 'icons'))
|
||||
icons = [os.path.join(icondir, subdir, filename) for subdir in
|
||||
os.listdir(icondir)]
|
||||
except OSError:
|
||||
try:
|
||||
# pysol06.png is known to have transparent borders around it
|
||||
# which is unsuitable for a window icon
|
||||
icon_blacklist = ('pysol06.png',)
|
||||
miscdir = self.findDir(os.path.join('images', 'misc'))
|
||||
icons = [os.path.join(miscdir, f) for f in os.listdir(miscdir)
|
||||
if f not in icon_blacklist]
|
||||
except OSError:
|
||||
icons = []
|
||||
return filter(os.path.isfile, icons)
|
||||
|
||||
def findDir(self, filename, subdirs=None):
|
||||
return self.__findFile(os.path.isdir, filename, subdirs)
|
||||
|
|
|
@ -25,11 +25,6 @@ from pysollib.macosx.appSupport import hideTkConsole
|
|||
from pysollib.settings import TOOLKIT, USE_TILE
|
||||
from pysollib.winsystems.common import BaseTkSettings, base_init_root_window
|
||||
|
||||
from six.moves import tkinter
|
||||
|
||||
if USE_TILE:
|
||||
from pysollib.tile import ttk
|
||||
|
||||
|
||||
def init_root_window(root, app):
|
||||
base_init_root_window(root, app)
|
||||
|
@ -38,15 +33,7 @@ def init_root_window(root, app):
|
|||
if TOOLKIT == 'gtk':
|
||||
pass
|
||||
elif USE_TILE:
|
||||
style = ttk.Style(root)
|
||||
color = style.lookup('.', 'background')
|
||||
if color:
|
||||
root.tk_setPalette(color) # for non-ttk widgets
|
||||
|
||||
if app.opt.tile_theme == 'aqua':
|
||||
# standard Tk scrollbars work on OS X, but ttk ones look weird
|
||||
ttk.Scrollbar = tkinter.Scrollbar
|
||||
|
||||
pass
|
||||
else: # pure Tk
|
||||
# root.option_add(...)
|
||||
pass
|
||||
|
|
|
@ -32,8 +32,9 @@ from pysollib.settings import TOOLKIT, USE_TILE
|
|||
from pysollib.settings import VERSION
|
||||
|
||||
if TOOLKIT == 'tk':
|
||||
from pysollib.ui.tktile.tkutil import loadImage
|
||||
if USE_TILE:
|
||||
from pysollib.tile import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
|
||||
def init_tile(app, top):
|
||||
|
@ -89,6 +90,15 @@ def base_init_root_window(root, app):
|
|||
# root.wm_group(root)
|
||||
root.wm_title(TITLE + ' ' + VERSION)
|
||||
root.wm_iconname(TITLE + ' ' + VERSION)
|
||||
|
||||
if TOOLKIT == 'tk':
|
||||
icons = [loadImage(img) for img in app.dataloader.findAllIconSizes()]
|
||||
if icons:
|
||||
try:
|
||||
root.wm_iconphoto(True, *icons)
|
||||
except AttributeError:
|
||||
root.tk.call('wm', 'iconphoto', root, '-default', *icons)
|
||||
|
||||
# set minsize
|
||||
sw, sh = (root.winfo_screenwidth(), root.winfo_screenheight())
|
||||
if sw < 640 or sh < 480:
|
||||
|
|
|
@ -25,7 +25,7 @@ from pysollib.settings import TOOLKIT, USE_TILE
|
|||
from pysollib.winsystems.common import BaseTkSettings, base_init_root_window
|
||||
|
||||
if USE_TILE:
|
||||
from pysollib.tile import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
|
||||
def init_root_window(root, app):
|
||||
|
|
|
@ -33,7 +33,7 @@ from pysollib.winsystems.common import \
|
|||
|
||||
if TOOLKIT == 'tk':
|
||||
if USE_TILE:
|
||||
from pysollib.tile import ttk
|
||||
from six.moves import tkinter_ttk as ttk
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
|
@ -44,10 +44,6 @@ def init_root_window(root, app):
|
|||
|
||||
base_init_root_window(root, app)
|
||||
|
||||
# if TOOLKIT == 'tk':
|
||||
# window.wm_iconbitmap("@"+filename)
|
||||
# window.wm_iconmask("@"+filename)
|
||||
|
||||
# root.self.wm_maxsize(9999, 9999) # unlimited
|
||||
if TOOLKIT == 'gtk':
|
||||
pass
|
||||
|
|
1
setup.py
1
setup.py
|
@ -50,7 +50,6 @@ for d in ddirs:
|
|||
data_files += get_data_files('locale', locale_dir)
|
||||
|
||||
if os.name == 'posix':
|
||||
data_files.append(('share/pixmaps', ['data/pysol.xbm', 'data/pysol.xpm']))
|
||||
for size in os.listdir('data/images/icons'):
|
||||
data_files.append(('share/icons/hicolor/%s/apps' % size,
|
||||
['data/images/icons/%s/pysol.png' % size]))
|
||||
|
|
|
@ -3,6 +3,7 @@ import unittest
|
|||
|
||||
from pysollib.acard import AbstractCard
|
||||
from pysollib.kpat_load_save import KpatXmlEmitter
|
||||
from pysollib.kpat_load_save import KpatEmitter
|
||||
|
||||
from six.moves import cStringIO
|
||||
|
||||
|
@ -83,3 +84,5 @@ class MyTests(unittest.TestCase):
|
|||
+ "<card id=\"1004\" suit=\"hearts\" " +
|
||||
"rank=\"queen\" turn=\"face-up\"/>\n"
|
||||
)
|
||||
e = KpatEmitter(f)
|
||||
self.assertTrue(e)
|
||||
|
|
|
@ -53,10 +53,12 @@
|
|||
# imports
|
||||
import unittest
|
||||
|
||||
from pysol_cards.cards import ms_rearrange
|
||||
from pysol_cards.cards import CardRenderer
|
||||
from pysol_cards.deal_game import Game
|
||||
from pysol_cards.random_base import RandomBase
|
||||
|
||||
# So the localpaths will be overrided.
|
||||
from pysollib.pysolrandom import LCRandom31, constructRandom, \
|
||||
from pysollib.pysolrandom import constructRandom, \
|
||||
random__long2str, random__str2long
|
||||
|
||||
# PySol imports
|
||||
|
@ -68,497 +70,7 @@ from pysollib.pysolrandom import LCRandom31, constructRandom, \
|
|||
# ************************************************************************/
|
||||
|
||||
|
||||
class Card:
|
||||
|
||||
ACE = 1
|
||||
KING = 13
|
||||
|
||||
def __init__(self, id, rank, suit, print_ts):
|
||||
self.id = id
|
||||
self.rank = rank
|
||||
self.suit = suit
|
||||
self.flipped = False
|
||||
self.print_ts = print_ts
|
||||
self.empty = False
|
||||
|
||||
def is_king(self):
|
||||
return self.rank == self.KING
|
||||
|
||||
def is_ace(self):
|
||||
return self.rank == self.ACE
|
||||
|
||||
def rank_s(self):
|
||||
s = "0A23456789TJQK"[self.rank]
|
||||
if (not self.print_ts) and s == "T":
|
||||
s = "10"
|
||||
return s
|
||||
|
||||
def suit_s(self):
|
||||
return "CSHD"[self.suit]
|
||||
|
||||
def to_s(self):
|
||||
if self.empty:
|
||||
return "-"
|
||||
ret = ""
|
||||
ret = ret + self.rank_s()
|
||||
ret = ret + self.suit_s()
|
||||
if self.flipped:
|
||||
ret = "<" + ret + ">"
|
||||
|
||||
return ret
|
||||
|
||||
def found_s(self):
|
||||
return self.suit_s() + "-" + self.rank_s()
|
||||
|
||||
def flip(self, flipped=True):
|
||||
new_card = Card(self.id, self.rank, self.suit, self.print_ts)
|
||||
new_card.flipped = flipped
|
||||
return new_card
|
||||
|
||||
def is_empty(self):
|
||||
return self.empty
|
||||
|
||||
|
||||
class Columns:
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
cols = []
|
||||
for i in range(num):
|
||||
cols.append([])
|
||||
|
||||
self.cols = cols
|
||||
|
||||
def add(self, idx, card):
|
||||
self.cols[idx].append(card)
|
||||
|
||||
def rev(self):
|
||||
self.cols.reverse()
|
||||
|
||||
def output(self):
|
||||
s = ''
|
||||
for column in self.cols:
|
||||
s += column_to_string(column) + "\n"
|
||||
return s
|
||||
|
||||
|
||||
class Board:
|
||||
def __init__(self, num_columns, with_freecells=False,
|
||||
with_talon=False, with_foundations=False):
|
||||
self.with_freecells = with_freecells
|
||||
self.with_talon = with_talon
|
||||
self.with_foundations = with_foundations
|
||||
self.columns = Columns(num_columns)
|
||||
if (self.with_freecells):
|
||||
self.freecells = []
|
||||
if (self.with_talon):
|
||||
self.talon = []
|
||||
if (self.with_foundations):
|
||||
self.foundations = map(lambda s: empty_card(), range(4))
|
||||
|
||||
def reverse_cols(self):
|
||||
return self.columns.rev()
|
||||
|
||||
def add(self, idx, card):
|
||||
return self.columns.add(idx, card)
|
||||
|
||||
def print_freecells(self):
|
||||
return "FC: " + column_to_string(self.freecells)
|
||||
|
||||
def print_talon(self):
|
||||
return "Talon: " + column_to_string(self.talon)
|
||||
|
||||
def print_foundations(self):
|
||||
cells = []
|
||||
for f in [2, 0, 3, 1]:
|
||||
if not self.foundations[f].is_empty():
|
||||
cells.append(self.foundations[f].found_s())
|
||||
|
||||
if len(cells):
|
||||
return "Foundations:" + ("".join(map(lambda s: " "+s, cells)))
|
||||
|
||||
def output(self):
|
||||
s = ''
|
||||
if (self.with_talon):
|
||||
s += self.print_talon() + "\n"
|
||||
if (self.with_foundations):
|
||||
s += self.print_foundations() + "\n"
|
||||
if (self.with_freecells):
|
||||
s += self.print_freecells() + "\n"
|
||||
s += self.columns.output()
|
||||
|
||||
return s
|
||||
|
||||
def add_freecell(self, card):
|
||||
if not self.with_freecells:
|
||||
raise AttributeError("Layout does not have freecells!")
|
||||
self.freecells.append(card)
|
||||
|
||||
def add_talon(self, card):
|
||||
if not self.with_talon:
|
||||
raise AttributeError("Layout does not have a talon!")
|
||||
|
||||
self.talon.append(card)
|
||||
|
||||
def put_into_founds(self, card):
|
||||
if not self.with_foundations:
|
||||
raise AttributeError("Layout does not have foundations!")
|
||||
|
||||
if ((self.foundations[card.suit].rank+1) == card.rank):
|
||||
self.foundations[card.suit] = card
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
self.talon.append(card)
|
||||
|
||||
|
||||
def empty_card():
|
||||
ret = Card(0, 0, 0, 1)
|
||||
ret.empty = True
|
||||
return ret
|
||||
|
||||
|
||||
def createCards(num_decks, print_ts):
|
||||
cards = []
|
||||
for deck in range(num_decks):
|
||||
id = 0
|
||||
for suit in range(4):
|
||||
for rank in range(13):
|
||||
cards.append(Card(id, rank+1, suit, print_ts))
|
||||
id = id + 1
|
||||
return cards
|
||||
|
||||
|
||||
def column_to_list_of_strings(col):
|
||||
return map(lambda c: c.to_s(), col)
|
||||
|
||||
|
||||
def column_to_string(col):
|
||||
return " ".join(column_to_list_of_strings(col))
|
||||
|
||||
|
||||
def flip_card(card_str, flip):
|
||||
if flip:
|
||||
return "<" + card_str + ">"
|
||||
else:
|
||||
return card_str
|
||||
|
||||
|
||||
def shuffle(orig_cards, rand):
|
||||
shuffled_cards = list(orig_cards)
|
||||
if isinstance(rand, LCRandom31):
|
||||
shuffled_cards = ms_rearrange(shuffled_cards)
|
||||
# rand.shuffle works in place
|
||||
rand.shuffle(shuffled_cards)
|
||||
return shuffled_cards
|
||||
|
||||
|
||||
class Game:
|
||||
REVERSE_MAP = \
|
||||
{
|
||||
"freecell":
|
||||
["freecell", "forecell", "bakers_game",
|
||||
"ko_bakers_game", "kings_only_bakers_game",
|
||||
"relaxed_freecell", "eight_off"],
|
||||
"der_katz":
|
||||
["der_katz", "der_katzenschwantz", "die_schlange"],
|
||||
"seahaven":
|
||||
["seahaven_towers", "seahaven", "relaxed_seahaven",
|
||||
"relaxed_seahaven_towers"],
|
||||
"bakers_dozen": None,
|
||||
"gypsy": None,
|
||||
"klondike": ["klondike", "klondike_by_threes",
|
||||
"casino_klondike", "small_harp",
|
||||
"thumb_and_pouch", "vegas_klondike", "whitehead"],
|
||||
"simple_simon": None,
|
||||
"yukon": None,
|
||||
"beleaguered_castle": ["beleaguered_castle",
|
||||
"streets_and_alleys", "citadel"],
|
||||
"fan": None,
|
||||
"black_hole": None,
|
||||
"all_in_a_row": None,
|
||||
}
|
||||
|
||||
def __init__(self, game_id, rand, print_ts):
|
||||
mymap = {}
|
||||
for k in self.REVERSE_MAP.keys():
|
||||
if self.REVERSE_MAP[k] is None:
|
||||
mymap[k] = k
|
||||
else:
|
||||
for alias in self.REVERSE_MAP[k]:
|
||||
mymap[alias] = k
|
||||
self.games_map = mymap
|
||||
self.game_id = game_id
|
||||
self.print_ts = print_ts
|
||||
self.rand = rand
|
||||
|
||||
def print_layout(self):
|
||||
game_class = self.lookup()
|
||||
|
||||
if not game_class:
|
||||
raise ValueError("Unknown game type " + self.game_id + "\n")
|
||||
|
||||
self.deal()
|
||||
|
||||
getattr(self, game_class)()
|
||||
|
||||
return self.board.output()
|
||||
|
||||
def lookup(self):
|
||||
return self.games_map[self.game_id]
|
||||
|
||||
def is_two_decks(self):
|
||||
return self.game_id in ("der_katz", "der_katzenschwantz",
|
||||
"die_schlange", "gypsy")
|
||||
|
||||
def get_num_decks(self):
|
||||
if self.is_two_decks():
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
def deal(self):
|
||||
orig_cards = createCards(self.get_num_decks(), self.print_ts)
|
||||
|
||||
orig_cards = shuffle(orig_cards, self.rand)
|
||||
|
||||
cards = orig_cards
|
||||
cards.reverse()
|
||||
|
||||
self.cards = cards
|
||||
self.card_idx = 0
|
||||
return True
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def no_more_cards(self):
|
||||
return self.card_idx >= len(self.cards)
|
||||
|
||||
def next(self):
|
||||
if self.no_more_cards():
|
||||
raise StopIteration
|
||||
c = self.cards[self.card_idx]
|
||||
self.card_idx = self.card_idx + 1
|
||||
return c
|
||||
|
||||
def new_cards(self, cards):
|
||||
self.cards = cards
|
||||
self.card_idx = 0
|
||||
|
||||
def add(self, idx, card):
|
||||
return self.board.add(idx, card)
|
||||
|
||||
def add_freecell(self, card):
|
||||
return self.board.add_freecell(card)
|
||||
|
||||
def cyclical_deal(game, num_cards, num_cols, flipped=False):
|
||||
for i in range(num_cards):
|
||||
game.add(i % num_cols, game.next().flip(flipped=flipped))
|
||||
return i
|
||||
|
||||
def add_all_to_talon(game):
|
||||
for card in game:
|
||||
game.board.add_talon(card)
|
||||
|
||||
# These are the games variants:
|
||||
# Each one is a callback.
|
||||
def der_katz(game):
|
||||
if (game.game_id == "die_schlange"):
|
||||
return "Foundations: H-A S-A D-A C-A H-A S-A D-A C-A"
|
||||
|
||||
game.board = Board(9)
|
||||
col_idx = 0
|
||||
|
||||
for card in game:
|
||||
if card.is_king():
|
||||
col_idx = col_idx + 1
|
||||
if not ((game.game_id == "die_schlange") and (card.rank == 1)):
|
||||
game.add(col_idx, card)
|
||||
|
||||
def freecell(game):
|
||||
is_fc = (game.game_id in ('forecell', 'eight_off'))
|
||||
|
||||
game.board = Board(8, with_freecells=is_fc)
|
||||
|
||||
if is_fc:
|
||||
game.cyclical_deal(48, 8)
|
||||
for card in game:
|
||||
game.add_freecell(card)
|
||||
if game.game_id == "eight_off":
|
||||
game.add_freecell(empty_card())
|
||||
else:
|
||||
game.cyclical_deal(52, 8)
|
||||
|
||||
def seahaven(game):
|
||||
game.board = Board(10, with_freecells=True)
|
||||
|
||||
game.add_freecell(empty_card())
|
||||
|
||||
game.cyclical_deal(50, 10)
|
||||
|
||||
for card in game:
|
||||
game.add_freecell(card)
|
||||
|
||||
def bakers_dozen(game):
|
||||
i, n = 0, 13
|
||||
kings = []
|
||||
cards = game.cards
|
||||
cards.reverse()
|
||||
for c in cards:
|
||||
if c.is_king():
|
||||
kings.append(i)
|
||||
i = i + 1
|
||||
for i in kings:
|
||||
j = i % n
|
||||
while j < i:
|
||||
if not cards[j].is_king():
|
||||
cards[i], cards[j] = cards[j], cards[i]
|
||||
break
|
||||
j = j + n
|
||||
|
||||
game.new_cards(cards)
|
||||
|
||||
game.board = Board(13)
|
||||
|
||||
game.cyclical_deal(52, 13)
|
||||
|
||||
def gypsy(game):
|
||||
num_cols = 8
|
||||
game.board = Board(num_cols, with_talon=True)
|
||||
|
||||
game.cyclical_deal(num_cols*2, num_cols, flipped=True)
|
||||
game.cyclical_deal(num_cols, num_cols, flipped=False)
|
||||
|
||||
game.add_all_to_talon()
|
||||
|
||||
def klondike(game):
|
||||
num_cols = 7
|
||||
game.board = Board(num_cols, with_talon=True)
|
||||
|
||||
for r in range(1, num_cols):
|
||||
for s in range(num_cols-r):
|
||||
game.add(s, game.next().flip())
|
||||
|
||||
game.cyclical_deal(num_cols, num_cols)
|
||||
|
||||
game.add_all_to_talon()
|
||||
|
||||
if not (game.game_id == "small_harp"):
|
||||
game.board.reverse_cols()
|
||||
|
||||
def simple_simon(game):
|
||||
game.board = Board(10)
|
||||
|
||||
num_cards = 9
|
||||
|
||||
while num_cards >= 3:
|
||||
for s in range(num_cards):
|
||||
game.add(s, game.next())
|
||||
num_cards = num_cards - 1
|
||||
|
||||
for s in range(10):
|
||||
game.add(s, game.next())
|
||||
|
||||
def fan(game):
|
||||
game.board = Board(18)
|
||||
|
||||
game.cyclical_deal(52-1, 17)
|
||||
|
||||
game.add(17, game.next())
|
||||
|
||||
def _shuffleHookMoveSorter(self, cards, func, ncards):
|
||||
# note that we reverse the cards, so that smaller sort_orders
|
||||
# will be nearer to the top of the Talon
|
||||
sitems, i = [], len(cards)
|
||||
for c in cards[:]:
|
||||
select, sort_order = func(c)
|
||||
if select:
|
||||
cards.remove(c)
|
||||
sitems.append((sort_order, i, c))
|
||||
if len(sitems) >= ncards:
|
||||
break
|
||||
i = i - 1
|
||||
sitems.sort()
|
||||
sitems.reverse()
|
||||
scards = map(lambda item: item[2], sitems)
|
||||
return cards, scards
|
||||
|
||||
def _shuffleHookMoveToBottom(self, cards, func, ncards=999999):
|
||||
# move cards to bottom of the Talon (i.e. last cards to be dealt)
|
||||
cards, scards = self._shuffleHookMoveSorter(cards, func, ncards)
|
||||
ret = scards + cards
|
||||
return ret
|
||||
|
||||
def _shuffleHookMoveToTop(self, cards, func, ncards=999999):
|
||||
# move cards to top of the Talon (i.e. last cards to be dealt)
|
||||
cards, scards = self._shuffleHookMoveSorter(cards, func, ncards)
|
||||
return cards + scards
|
||||
|
||||
def black_hole(game):
|
||||
game.board = Board(17)
|
||||
|
||||
# move Ace to bottom of the Talon (i.e. last cards to be dealt)
|
||||
game.cards = game._shuffleHookMoveToBottom(
|
||||
game.cards, lambda c: (c.id == 13, c.suit), 1)
|
||||
game.next()
|
||||
game.cyclical_deal(52-1, 17)
|
||||
|
||||
return "Foundations: AS"
|
||||
|
||||
def all_in_a_row(game):
|
||||
game.board = Board(13)
|
||||
|
||||
# move Ace to bottom of the Talon (i.e. last cards to be dealt)
|
||||
game.cards = game._shuffleHookMoveToTop(
|
||||
game.cards, lambda c: (c.id == 13, c.suit), 1)
|
||||
game.cyclical_deal(52, 13)
|
||||
return "Foundations: -"
|
||||
|
||||
def beleaguered_castle(game):
|
||||
aces_up = game.game_id in ("beleaguered_castle", "citadel")
|
||||
|
||||
game.board = Board(8, with_foundations=True)
|
||||
|
||||
if aces_up:
|
||||
new_cards = []
|
||||
|
||||
for c in game:
|
||||
if c.is_ace():
|
||||
game.board.put_into_founds(c)
|
||||
else:
|
||||
new_cards.append(c)
|
||||
|
||||
game.new_cards(new_cards)
|
||||
|
||||
for i in range(6):
|
||||
for s in range(8):
|
||||
c = game.next()
|
||||
if (game.game_id == "citadel") and \
|
||||
game.board.put_into_founds(c):
|
||||
# Already dealt with this card
|
||||
True
|
||||
else:
|
||||
game.add(s, c)
|
||||
if game.no_more_cards():
|
||||
break
|
||||
|
||||
if (game.game_id == "streets_and_alleys"):
|
||||
game.cyclical_deal(4, 4)
|
||||
|
||||
def yukon(game):
|
||||
num_cols = 7
|
||||
game.board = Board(num_cols)
|
||||
|
||||
for i in range(1, num_cols):
|
||||
for j in range(i, num_cols):
|
||||
game.add(j, game.next().flip())
|
||||
|
||||
for i in range(4):
|
||||
for j in range(1, num_cols):
|
||||
game.add(j, game.next())
|
||||
|
||||
game.cyclical_deal(num_cols, num_cols)
|
||||
ren = CardRenderer(True)
|
||||
|
||||
|
||||
class MyTests(unittest.TestCase):
|
||||
|
@ -569,11 +81,9 @@ class MyTests(unittest.TestCase):
|
|||
|
||||
def test_main(self):
|
||||
|
||||
rand = constructRandom('24')
|
||||
|
||||
def test_24(blurb):
|
||||
game = Game("freecell", rand, True)
|
||||
got_s = game.print_layout()
|
||||
game = Game("freecell", 24, RandomBase.DEALS_PYSOLFC)
|
||||
got_s = game.calc_layout_string(ren)
|
||||
self.assertEqual(got_s, '''4C 2C 9C 8C QS 4S 2H
|
||||
5H QH 3C AC 3H 4H QD
|
||||
QC 9S 6H 9H 3S KS 3D
|
||||
|
@ -586,14 +96,10 @@ AH 5S 6S AD 8H JD
|
|||
|
||||
# TEST
|
||||
test_24('Deal 24')
|
||||
rand.reset()
|
||||
# TEST
|
||||
test_24('MS deal after reset.')
|
||||
|
||||
rand = constructRandom('ms123456')
|
||||
game = Game("freecell", rand, True)
|
||||
game = Game("freecell", 123456, RandomBase.DEALS_MS)
|
||||
# TEST
|
||||
got_s = game.print_layout()
|
||||
got_s = game.calc_layout_string(ren)
|
||||
self.assertEqual(got_s, '''QD TC AS KC AH KH 6H
|
||||
6D TD 8D TH 7C 2H 9C
|
||||
AC AD 5C 5H 8C 9H 9D
|
||||
|
@ -604,10 +110,9 @@ JS 8S 4D 4C 2S 7D 3C
|
|||
JD QH 6S 4H QC 8H
|
||||
''', 'Microsoft Deal 123456')
|
||||
|
||||
rand = constructRandom('123456')
|
||||
game = Game("freecell", rand, True)
|
||||
game = Game("freecell", 123456, True)
|
||||
# TEST
|
||||
self._cmp_board(game.print_layout(), '''3D 6C AS TS QC 8D 4D
|
||||
self._cmp_board(game.calc_layout_string(ren), '''3D 6C AS TS QC 8D 4D
|
||||
2D TC 4H JD TD 2H 5C
|
||||
2C 8S AH KD KH 5S 7C
|
||||
9C 8C QH 3C 5D 9S QD
|
||||
|
@ -618,9 +123,9 @@ KC JS 9H 4S 7S AD
|
|||
''', 'PySolFC deal No. 123456')
|
||||
|
||||
rand = constructRandom('ms3000000000')
|
||||
game = Game("freecell", rand, True)
|
||||
game = Game("freecell", 3000000000, RandomBase.DEALS_MS)
|
||||
# TEST
|
||||
self._cmp_board(game.print_layout(), '''8D TS JS TD JH JD JC
|
||||
self._cmp_board(game.calc_layout_string(ren), '''8D TS JS TD JH JD JC
|
||||
4D QS TH AD 4S TC 3C
|
||||
9H KH QH 4C 5C KD AS
|
||||
9D 5D 8S 4H KS 6S 9S
|
||||
|
@ -631,9 +136,9 @@ KC JS 9H 4S 7S AD
|
|||
''', 'Microsoft Deal #3E9 - long seed.')
|
||||
|
||||
rand = constructRandom('ms6000000000')
|
||||
game = Game("freecell", rand, True)
|
||||
game = Game("freecell", 6000000000, RandomBase.DEALS_MS)
|
||||
# TEST
|
||||
got_s = game.print_layout()
|
||||
got_s = game.calc_layout_string(ren)
|
||||
self.assertEqual(got_s, '''2D 2C QS 8D KD 8C 4C
|
||||
3D AH 2H 4H TS 6H QD
|
||||
4D JS AD 6S JH JC JD
|
||||
|
@ -669,9 +174,9 @@ QH 9H 9D 5S 7S 6C
|
|||
# TEST
|
||||
self.assertEqual(seed, 'ms100001', 'increaseSeed for ms deals')
|
||||
rand = constructRandom(seed)
|
||||
game = Game("freecell", rand, True)
|
||||
game = Game("freecell", int(seed[2:]), RandomBase.DEALS_MS)
|
||||
# TEST
|
||||
self._cmp_board(game.print_layout(), '''5S AH 4H TD 4S JD JS
|
||||
self._cmp_board(game.calc_layout_string(ren), '''5S AH 4H TD 4S JD JS
|
||||
3C 8C 4C AC JC AS QS
|
||||
7C QH 2D QD 8S 9D AD
|
||||
KS 7S 5H 3H TS 3S 5D
|
||||
|
|
Loading…
Add table
Reference in a new issue