1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-15 02:54:09 -04:00

Compare commits

...

37 commits

Author SHA1 Message Date
Shlomi Fish
513726360d fix flake8 validation 2020-05-06 15:22:47 +03:00
Shlomi Fish
52d37cfeae fix merged code 2020-05-06 15:13:08 +03:00
Shlomi Fish
c7a3907d25 add missing attribute.
See https://github.com/shlomif/PySolFC/issues/129 .
2020-05-06 15:13:08 +03:00
Shlomi Fish
8244d19e6e start writing the kpat emitter. 2020-05-06 15:13:05 +03:00
Shlomi Fish
70123aaa5d Require a minimal version of "attr".
See: https://github.com/shlomif/PySolFC/issues/159 . Thanks to
@radub7 for the report.
2020-05-06 15:11:25 +03:00
Shlomi Fish
9666c56a9f Python2 compatibility: travis rebuild for new pysol-cards 2020-05-06 15:11:25 +03:00
Shlomi Fish
6f591dd519 typo 2020-05-06 15:11:25 +03:00
Shlomi Fish
bce3354f3d move to upstream PyPI pysol_cards 2020-05-06 15:11:25 +03:00
Shlomi Fish
abc9c2c3aa move to upstream PyPI pysol_cards 2020-05-06 15:11:25 +03:00
Shlomi Fish
90d36213e8 move to upstream PyPI pysol_cards 2020-05-06 15:11:25 +03:00
Shlomi Fish
b90b3ed1e1 move to upstream PyPI pysol_cards 2020-05-06 15:11:25 +03:00
Juhani Numminen
8dbd5c552f Retire pysol.xbm & .xpm & the unused code that uses them 2020-05-06 15:11:25 +03:00
Shlomi Fish
bf620c6310 new 2.8.0 release 2020-05-06 15:11:25 +03:00
Juhani Numminen
cc9adb6813 travis osx: Install python3 from python.org & build Pillow from source
Unlike python of Homebrew, the python.org version comes bundled with
a recent Tk that is much less buggy than macOS stock Tk.

py2app does not seem to work with Pillow's pypi wheels.
2020-05-06 15:11:25 +03:00
Juhani Numminen
18d3b2713a Work around Playable Preview launching unresponsive help window 2020-05-06 15:11:25 +03:00
Juhani Numminen
0a220861a9 Playable preview: swap incorrect actions for Rules and Cancel buttons
Line 435 assigns id 10 (not 1) to the Rules button.
2020-05-06 15:11:25 +03:00
Juhani Numminen
42ede36daf No such thing as pysollib.app.comments (#153) 2020-05-06 15:11:25 +03:00
Juhani Numminen
451e5a5003 Fix command line option error message
TypeError: unsupported operand type(s) for +: 'GetoptError' and 'str'
caused by commit 65b86f2d1d
2020-05-06 15:11:25 +03:00
Juhani Numminen
928c8a2819 Fix py2 compatibility: wm_iconphoto does not exist in python2.7 2020-05-06 15:11:25 +03:00
Juhani Numminen
89a6728bf8 Load icons from either images/misc or images/icons 2020-05-06 15:11:25 +03:00
Juhani Numminen
55edd37a72 Tk: set window icon
make_help_toplevel changed from Tk to Toplevel since Tk creates another
Tcl interpreter, causing errors like this:
'_tkinter.TclError: can't use "pyimage228" as iconphoto: not a photo image'

1024x1024 logo file is renamed because it has large margins on each side
which is unsuitable for a window icon.
2020-05-06 15:11:25 +03:00
Shlomi Fish
c6d093d995 Prepare for 2.8.0 release.
See https://github.com/shlomif/PySolFC/issues/151 .

Not releasing yet.
2020-05-06 15:11:25 +03:00
lufebe16
b92091d1c3 Apk build scripts
- refactoring
2020-05-06 15:11:25 +03:00
Shlomi Fish
b26786a877 appveyor: fix invocation 2020-05-06 15:11:24 +03:00
Shlomi Fish
4b7df34d67 pin the fcs version - reproducible builds 2020-05-06 15:11:24 +03:00
Shlomi Fish
a004f7e690 fix tidyall/flake8 tests 2020-05-06 15:11:24 +03:00
lufebe16
ccacdbe148 Apk build scripts
- updated for use with p4a version 2019.10.6.
- additions to build instructions.
2020-05-06 15:11:24 +03:00
lufebe16
9169297801 Updates kivy version:
- added support for right mouse button (desktop only)
- corrected a problem with virtualenv in mkp4a.init
2020-05-06 15:11:24 +03:00
Shlomi Fish
586cd4a05f Add python3 PATH for pip3 on macOS.
The travis-CI builds have been failing and this is an attempt to fix them.
2020-05-06 15:11:24 +03:00
Shlomi Fish
ce72838aff Add python3 to homebrew on macOS.
The travis-CI builds have been failing and this is an attempt to fix them.
2020-05-06 15:11:24 +03:00
Shlomi Fish
3649a52ab2 SHOULD_PUT_bye_for_now_FIRST = False
To avoid complaints about non random music.
2020-05-06 15:11:24 +03:00
Juhani Numminen
3f660b25a0 Import ttk from standard library, drop local copy 2020-05-06 15:11:24 +03:00
Juhani Numminen
ca93af4897 Fix whitespace in pyramid.py 2020-05-06 15:11:24 +03:00
lufebe16
2fea665242 Corrections on game 'pyramid'
- For TypeError reported with #147. (clickhandler failed to return a value)
- For index out of range (in pyramid.y)
2020-05-06 15:11:24 +03:00
Juhani Numminen
d0ff5494d5 Fix 'TclError: unknown option' for tk
Partly reverts commit b9a46ecf35
2020-05-06 15:11:24 +03:00
Juhani Numminen
3793d8392e Do not catch exceptions too broadly in mainloop
StopIteration seems to be the expected way to stop, at least
for the tk and tile interfaces.
2020-05-06 15:11:24 +03:00
Juhani Numminen
0fd5e40b54 Fix menu colors on macOS with dark menu bar
Widget colors and all scrollbars look just fine without these lines
despite the comments.
2020-05-06 15:11:24 +03:00
52 changed files with 214 additions and 2251 deletions

View file

@ -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\

View file

@ -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

View file

@ -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"

View file

@ -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
##

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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'

View file

@ -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"

View file

@ -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'

View file

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View file

@ -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
};

View file

@ -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&&&&&&&&&&&&&",
"&&&&&&&&&&&&&. .&&&&&&&&&&&&&",
"&&&&&&&&&&&$. .$&&&&&&&&&&&",
"&&&&&&&&&&&$+++@@@++$&&&&&&&&&&&",
"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
};

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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,

View file

@ -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

View file

@ -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)

View file

@ -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):

View file

@ -1,4 +1,5 @@
from . import ttk
from six.moves import tkinter_ttk as ttk
from .tkwidget import MfxDialog

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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__':

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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,

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -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):

View file

@ -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

View file

@ -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]))

View file

@ -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)

View file

@ -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