diff --git a/.cache/naucse/repo/HEAD b/.cache/naucse/repo/HEAD new file mode 100644 index 00000000..b870d826 --- /dev/null +++ b/.cache/naucse/repo/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/.cache/naucse/repo/config b/.cache/naucse/repo/config new file mode 100644 index 00000000..e6da2315 --- /dev/null +++ b/.cache/naucse/repo/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + ignorecase = true + precomposeunicode = true diff --git a/.cache/naucse/repo/description b/.cache/naucse/repo/description new file mode 100644 index 00000000..498b267a --- /dev/null +++ b/.cache/naucse/repo/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/.cache/naucse/repo/hooks/applypatch-msg.sample b/.cache/naucse/repo/hooks/applypatch-msg.sample new file mode 100755 index 00000000..a5d7b84a --- /dev/null +++ b/.cache/naucse/repo/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/.cache/naucse/repo/hooks/commit-msg.sample b/.cache/naucse/repo/hooks/commit-msg.sample new file mode 100755 index 00000000..b58d1184 --- /dev/null +++ b/.cache/naucse/repo/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/.cache/naucse/repo/hooks/fsmonitor-watchman.sample b/.cache/naucse/repo/hooks/fsmonitor-watchman.sample new file mode 100755 index 00000000..23e856f5 --- /dev/null +++ b/.cache/naucse/repo/hooks/fsmonitor-watchman.sample @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + my $last_update_line = ""; + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + $last_update_line = qq[\n"since": $last_update_token,]; + } + my $query = <<" END"; + ["query", "$git_work_tree", {$last_update_line + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/.cache/naucse/repo/hooks/post-update.sample b/.cache/naucse/repo/hooks/post-update.sample new file mode 100755 index 00000000..ec17ec19 --- /dev/null +++ b/.cache/naucse/repo/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/.cache/naucse/repo/hooks/pre-applypatch.sample b/.cache/naucse/repo/hooks/pre-applypatch.sample new file mode 100755 index 00000000..4142082b --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/.cache/naucse/repo/hooks/pre-commit.sample b/.cache/naucse/repo/hooks/pre-commit.sample new file mode 100755 index 00000000..e144712c --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/.cache/naucse/repo/hooks/pre-merge-commit.sample b/.cache/naucse/repo/hooks/pre-merge-commit.sample new file mode 100755 index 00000000..399eab19 --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/.cache/naucse/repo/hooks/pre-push.sample b/.cache/naucse/repo/hooks/pre-push.sample new file mode 100755 index 00000000..4ce688d3 --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/.cache/naucse/repo/hooks/pre-rebase.sample b/.cache/naucse/repo/hooks/pre-rebase.sample new file mode 100755 index 00000000..6cbef5c3 --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/.cache/naucse/repo/hooks/pre-receive.sample b/.cache/naucse/repo/hooks/pre-receive.sample new file mode 100755 index 00000000..a1fd29ec --- /dev/null +++ b/.cache/naucse/repo/hooks/pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/.cache/naucse/repo/hooks/prepare-commit-msg.sample b/.cache/naucse/repo/hooks/prepare-commit-msg.sample new file mode 100755 index 00000000..10fa14c5 --- /dev/null +++ b/.cache/naucse/repo/hooks/prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/.cache/naucse/repo/hooks/push-to-checkout.sample b/.cache/naucse/repo/hooks/push-to-checkout.sample new file mode 100755 index 00000000..af5a0c00 --- /dev/null +++ b/.cache/naucse/repo/hooks/push-to-checkout.sample @@ -0,0 +1,78 @@ +#!/bin/sh + +# An example hook script to update a checked-out tree on a git push. +# +# This hook is invoked by git-receive-pack(1) when it reacts to git +# push and updates reference(s) in its repository, and when the push +# tries to update the branch that is currently checked out and the +# receive.denyCurrentBranch configuration variable is set to +# updateInstead. +# +# By default, such a push is refused if the working tree and the index +# of the remote repository has any difference from the currently +# checked out commit; when both the working tree and the index match +# the current commit, they are updated to match the newly pushed tip +# of the branch. This hook is to be used to override the default +# behaviour; however the code below reimplements the default behaviour +# as a starting point for convenient modification. +# +# The hook receives the commit with which the tip of the current +# branch is going to be updated: +commit=$1 + +# It can exit with a non-zero status to refuse the push (when it does +# so, it must not modify the index or the working tree). +die () { + echo >&2 "$*" + exit 1 +} + +# Or it can make any necessary changes to the working tree and to the +# index to bring them to the desired state when the tip of the current +# branch is updated to the new commit, and exit with a zero status. +# +# For example, the hook can simply run git read-tree -u -m HEAD "$1" +# in order to emulate git fetch that is run in the reverse direction +# with git push, as the two-tree form of git read-tree -u -m is +# essentially the same as git switch or git checkout that switches +# branches while keeping the local changes in the working tree that do +# not interfere with the difference between the branches. + +# The below is a more-or-less exact translation to shell of the C code +# for the default behaviour for git's push-to-checkout hook defined in +# the push_to_deploy() function in builtin/receive-pack.c. +# +# Note that the hook will be executed from the repository directory, +# not from the working tree, so if you want to perform operations on +# the working tree, you will have to adapt your code accordingly, e.g. +# by adding "cd .." or using relative paths. + +if ! git update-index -q --ignore-submodules --refresh +then + die "Up-to-date check failed" +fi + +if ! git diff-files --quiet --ignore-submodules -- +then + die "Working directory has unstaged changes" +fi + +# This is a rough translation of: +# +# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +if git cat-file -e HEAD 2>/dev/null +then + head=HEAD +else + head=$(git hash-object -t tree --stdin &2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero=$(git hash-object --stdin &2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/.cache/naucse/repo/info/exclude b/.cache/naucse/repo/info/exclude new file mode 100644 index 00000000..a5196d1b --- /dev/null +++ b/.cache/naucse/repo/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 02ec9327..b1e289b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,21 +28,20 @@ jobs: poetry install -E ghp-compiled - name: Compile the courses run: | - for slug in pyladies mi-pyt meta lessons; do - poetry run python -m naucse_render compile _compiled/$slug --slug $slug \ + + poetry run python -m naucse_render compile _compiled \ --edit-repo-url https://github.com/${{ github.repository }} \ - --edit-repo-branch main - done - - if: ${{ github.ref == 'refs/heads/main' }} + --edit-repo-branch ${{ github.ref_name }} + - if: ${{ startsWith(github.ref, 'refs/heads/') }} name: Publish compiled courses run: | - git fetch origin compiled || : - poetry run python -m ghp_import -m "Compiled" -b compiled --push _compiled/ + git fetch origin compiled/${{ github.ref_name }} || : + poetry run python -m ghp_import -m "Compiled" -b compiled/${{ github.ref_name }} --push _compiled/ curl -H 'Content-Type: application/json' \ - --data '{"repository": "https://github.com/${{ github.repository }}", "branch": "compiled"}' \ + --data '{"repository": "https://github.com/${{ github.repository }}", "branch": "compiled/${{ github.ref_name }}"}' \ https://hooks.nauc.se/trigger env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GIT_AUTHOR_NAME: ghp_import GIT_COMMITTER_NAME: ghp_import - EMAIL: none@invalid + EMAIL: none@invalid \ No newline at end of file diff --git a/courses/meta.yml b/courses/meta.yml deleted file mode 100644 index a22cb37b..00000000 --- a/courses/meta.yml +++ /dev/null @@ -1,26 +0,0 @@ -title: Jak přidat kurz na Nauč se Python -description: Návod na přidání kurzu na Nauč se Python -long_description: | - - V tomto meta-kurzu je kompletní návod na přidávání kurzů na Nauč se Python. - - Kurz je určený pro organizátory a k přidání kurzu jsou potřeba jen základní znalosti práce - s gitem a GitHubem. -canonical: true -meta: true -sessions: -- title: Přidání kurzu - slug: adding-a-run - materials: - - lesson: meta/local-run - - lesson: meta/submitting-a-run - -extra_lessons: -- git/install -- git/branching -- git/git-collaboration-2in1 -- beginners/install -- beginners/cmdline -- beginners/venv-setup -- intro/notebook -- fast-track/yaml diff --git a/courses/mi-pyt.yml b/courses/mi-pyt.yml deleted file mode 100644 index bed632b2..00000000 --- a/courses/mi-pyt.yml +++ /dev/null @@ -1,88 +0,0 @@ -title: MI-PYT -description: Základy již znáte a chcete se dozvědět o dalších možnostech využití Pythonu. -long_description: | - Díváte se na materiály předmětu MI-PYT (Pokročilý Python) na FIT ČVUT. - Materiály jsou dostupné v dvojí podobě, interně pro studenty FITu - na *Course Pages* a pro všechny veřejně zde. - - Tento kurz vzniká pod záštitou firmy Red Hat Czech, s.r.o. - -canonical: true -vars: - coach-present: false - mi-pyt: true -sessions: -- title: Weboví klienti & CLI – Requests a Click - slug: requests-click - materials: - - lesson: fast-track/install - - lesson: intro/requests - - lesson: intro/click - -- title: Webové aplikace – Flask - slug: flask - materials: - - lesson: intro/flask - - lesson: intro/deployment - -- title: Moduly - slug: distribution - materials: - - lesson: intro/distribution - -- title: Testování - slug: testing - materials: - - lesson: intro/testing - -- title: Dokumentace – Sphinx - slug: docs - materials: - - lesson: intro/docs - -- title: NumPy - slug: numpy - materials: - - lesson: intro/notebook - - lesson: intro/numpy - - title: Tahák na NumPy - url: https://pyvec.github.io/cheatsheets/numpy/numpy-cs.pdf - type: cheatsheet - -- title: Analýza dat – Pandas - slug: pandas - materials: - - lesson: intro/pandas - -- title: Cython - slug: cython - materials: - - lesson: intro/cython - -- title: GUI – PyQT - slug: qt - materials: - - lesson: intro/pyqt - -- title: Generátory a AsyncIO - slug: async - materials: - - lesson: advanced/generators - - lesson: intro/async - -- title: Magie - slug: magic - materials: - - lesson: intro/magic - -- title: MicroPython - slug: upy - materials: - - lesson: intro/micropython - -extra_lessons: -- beginners/install -- beginners/cmdline -- beginners/testing -- fast-track/http -- git/git-collaboration-2in1 diff --git a/courses/ostrava2022_python.yml b/courses/ostrava2022_python.yml new file mode 100644 index 00000000..eec601ce --- /dev/null +++ b/courses/ostrava2022_python.yml @@ -0,0 +1,414 @@ +title: Začátečnický kurz Pythonu +subtitle: Ostrava - podzim 2022 +time: každou středu od 17:00 do 19:00 +default_time: + start: '17:00' + end: '19:00' +timezone: Europe/Prague +place: Tieto Towers, 28. října 3346/91, Moravská Ostrava, Ostrava +description: Naučte se Python vážně od začátku. Žádné předchozí znalosti nejsou potřeba. +long_description: | + Vítáme tě v Začátečnickém kurzu Pythonu od PyLadies Ostrava. + + Níže najdeš materiály ke kurzu, který začíná 12. 10. 2022. + + Jednotlivé lekce jsou určeny naprostým začátečnicím. Instrukce jsou uvedeny pro operační systémy Linux, Windows i macOS. + + Stránky samotných PyLadies najdeš na http://pyladies.cz. + + Domácí úkoly k jednotlivým lekcím najdeš na https://projekty.pyladies.cz/. + + Zpětnou vazbu na konci každé lekce můžeš zadat na https://zpetnovazebnik.pyladies.cz/. +vars: + pyladies: true + coach-present: true +sessions: +- slug: preparation + serial: 0 + title: Příprava před začátkem kurzu + materials: + + - lesson: adm/intro + + - title: Studijní materiály + + - lesson: beginners/cmdline + - lesson: beginners/install + - lesson: beginners/venv-setup + - lesson: beginners/install-editor + + - title: Videa ke shlédnutí + + - title: (13:50) Příkazová řádka - Linux & macOS - video + url: https://youtu.be/GGMg8O4hE0c + - title: (22:34) Příkazová řádka - Windows - video + url: https://youtu.be/kriVWJmXpZc + - title: (8:15) Instalace Pythonu - Linux - video + url: https://youtu.be/7Qi7cSkoBA0 + - title: (4:48) Instalace Pythonu - Windows - video + url: https://youtu.be/Xr6liKJzRGA + - title: (7:48) Nastavení prostředí - video + url: https://youtu.be/AFVvpfQB0V0 + - title: (8:01) Instalace editoru - video + url: https://youtu.be/JcXUCuneX04 + + - title: Další odkazy a materiály + + - title: Tahák na klávesnici (PDF) + url: https://pyvec.github.io/cheatsheets/keyboard/keyboard-cs.pdf + type: cheatsheet + +- slug: hello_world + title: První program + date: 2022-10-12 + materials: + + - title: Studijní materiály + + - lesson: beginners/first-steps + - lesson: beginners/hello-world + - lesson: beginners/print + - lesson: beginners/variables + + - title: Videa ke shlédnutí + + - title: (7:23) První krůčky - video + url: https://youtu.be/0Poe25XdKIA + - title: (7:50) První program - video + url: https://youtu.be/9aJcvOfleFs + - title: (12:38) Print a chybové hlášky - video + url: https://youtu.be/14-LVG9Edng + - title: (15:08) Proměnné - video + url: https://youtu.be/-zKws24FmCg + + - title: Další odkazy a materiály + + - title: Tahák na debuggování (PDF) + url: https://pyvec.github.io/cheatsheets/errors/errors-cs.pdf + type: cheatsheet + - lesson: feedback/fb_ov_lesson1 + +- slug: conditions + title: Podmínky a funkce + date: 2022-10-19 + materials: + + - title: Studijní materiály + + - lesson: beginners/expressions + - lesson: beginners/comparisons + - lesson: beginners/functions + - lesson: beginners/basic-functions + - lesson: beginners/and-or + + - title: Videa ke shlédnutí + + - title: (8:32) Vyhodnocování výrazů - video + url: https://youtu.be/nNXDXuN6Smg + - title: (17:27) Porovnání - video + url: https://youtu.be/Q1YJqWzfnck + - title: (17:26) Funkce - video + url: https://youtu.be/w_d8VKS8i48 + - title: (15:51) Užitečné funkce - video + url: https://youtu.be/udEOvGIuZrs + - title: (7:58) Nebo anebo a - video + url: https://youtu.be/5Mc-cgoaM10 + + - title: Další odkazy a materiály + + - title: Tahák na užitečné funkce (PDF) + url: https://pyvec.github.io/cheatsheets/basic-functions/basic-functions-cs.pdf + type: cheatsheet + +- slug: loops + title: Želva 🐢 a cykly + date: 2022-10-26 + materials: + + - title: Studijní materiály + + - lesson: intro/turtle + - lesson: beginners/while + - lesson: beginners/reassignment + + - title: Videa ke shlédnutí + + - title: (33:02) Želva a cykly - video + url: https://youtu.be/A__45ibnsnc + - title: (6:57) Cyklus While - video + url: https://youtu.be/I_mkND45dB0 + - title: (9:28) Přepisování proměnných - video + url: https://youtu.be/fFh1LiksgdA + + - title: Další odkazy a materiály + - lesson: feedback/fb_ov_lesson3 + +- slug: strings + title: Řetězce + date: 2022-11-02 + materials: + + - title: Studijní materiály + + - lesson: beginners/str + - lesson: beginners/str-index-slice + - lesson: beginners/str-methods + - lesson: beginners/fstring + + - title: Videa ke shlédnutí + + - title: (19:54) Zápis řetězců - video + url: https://youtu.be/xdZWIvryP20 + - title: (16:12) Výběr z řetězců - video + url: https://youtu.be/QOamCtzLPAo + - title: (9:19) Řetězcové funkce a metody - video + url: https://youtu.be/I3CB2YihRh8 + - title: (11:52) Šablony (formátovací řetězce) - video + url: https://youtu.be/2-r2e9aM1HU + + - title: Další odkazy a materiály + + - title: Řetězcový tahák (PDF) + url: https://pyvec.github.io/cheatsheets/strings/strings-cs.pdf + type: cheatsheet + +- slug: functions + title: Vlastní funkce + date: 2022-11-09 + materials: + + - title: Studijní materiály + + - lesson: beginners/def + - lesson: beginners/prefer-return + - lesson: beginners/nested-traceback + - lesson: beginners/local-variables + - lesson: beginners/recursion + + - title: Videa ke shlédnutí + + - title: (17:01) Definice funkcí - video + url: https://youtu.be/6a-RjR9fNBY + - title: (8:54) Vrátit nebo vypsat? - video + url: https://youtu.be/4M6Yq9ZyObs + - title: (3:58) Chybové hlášky ze zanořených funkcí - video + url: https://youtu.be/I_DpjBDQy_k + - title: (14:25) Lokální proměnné - video + url: https://youtu.be/gbjXnMmuAxw + - title: (12:23) Rekurze - video + url: https://youtu.be/Vu6_jK8OiAI + + + - title: Další odkazy a materiály + +- slug: exceptions + title: Chyby a moduly + date: 2022-11-16 + materials: + + - title: Studijní materiály + + - lesson: beginners/exceptions + - lesson: beginners/modules + - lesson: beginners/circular-imports + + - title: Videa ke shlédnutí + + - title: (17:11) Výjimky - video + url: https://youtu.be/s-P5mBjUJBw + - title: (10:32) Moduly - video + url: https://youtu.be/LFp_x7OgCBU + - title: (5:56) Cyklické importy - video + url: https://youtu.be/bZ4iiSDe_4g + + - title: Další odkazy a materiály + + - title: Výjimkový tahák (PDF) + url: https://pyvec.github.io/cheatsheets/exceptions/exceptions-cs.pdf + type: cheatsheet + +- slug: test + title: Rozhraní a testy + date: 2022-11-23 + materials: + + - title: Studijní materiály + + - lesson: beginners/interfaces + - lesson: beginners/testing + - lesson: beginners/main-module + + - title: Videa ke shlédnutí + + - title: (6:51) Rozhraní - video + url: https://youtu.be/xQ7J_R8VAJQ + - title: (15:25) Testování - video + url: https://youtu.be/-KS_VRerZQ0 + - title: (10:12) Spouštěcí moduly - video + url: https://youtu.be/S6HyFIUyPTw + - title: (3:53) Negativní testy - video + url: https://youtu.be/s-KPxX5ZcMs + + - title: Další odkazy a materiály + +- slug: list + title: Seznamy a n-tice + date: 2022-11-30 + materials: + + - title: Studijní materiály + + - lesson: beginners/list + - lesson: beginners/tuple + - lesson: beginners/nested-list + + - title: Videa ke shlédnutí + + - title: (11:10) Seznamy - Úvod - video + url: https://youtu.be/abIMJjb9Nxw + - title: (32:52) Co všechno umí seznamy - video + url: https://youtu.be/-gINX_pOtQo + - title: (9:51) N-tice - video + url: https://youtu.be/dObtA75Ouzk + + + - title: Další odkazy a materiály + + - lesson: beginners/vscode + +- slug: file + title: Sekvence a soubory + date: 2022-12-07 + materials: + + - title: Studijní materiály + + - lesson: beginners/range + - lesson: beginners/zip-enumerate + - lesson: beginners/files + - lesson: beginners/with + + + - title: Videa ke shlédnutí + + - title: (5:02) Range - video + url: https://youtu.be/UtyIKgsnLmk + - title: (16:39) Iterátory n-tic (enum & zip) - video + url: https://youtu.be/JUCb7iiGsW4 + - title: (13:17) Soubory - video + url: https://youtu.be/1X4TU_thg7s + + + - title: Další odkazy a materiály + +- slug: dict + title: Slovníky + date: 2022-12-14 + materials: + + - title: Studijní materiály + + - lesson: beginners/dict + - lesson: beginners/dict-with-list-values + - lesson: intro/json + - title: Ukázka jednoduchého API + type: lesson + url: http://pyladies.cz/v1/s011-dicts/simple-api.html + + - title: Videa ke shlédnutí + + - title: (19:23) Slovníky - video + url: https://youtu.be/OVnUhWwd2C0 + - title: (4:42) Více hodnot v jednom záznamu slovníku - video + url: https://youtu.be/pQ268T2IZCo + - title: (12:54) Kódování dat - JSON - video + url: https://youtu.be/nPUSH7gSI4o + + - title: Další odkazy a materiály + + - title: Slovníkový tahák (PDF) + url: https://pyvec.github.io/cheatsheets/dicts/dicts-cs.pdf + type: cheatsheet + +- slug: class + title: Třídy a dědičnost + date: 2022-12-21 + materials: + + - title: Studijní materiály + + - lesson: beginners/class + - lesson: beginners/inheritance + + - title: Videa ke shlédnutí + + - title: (22:26) Třídy - video + url: https://youtu.be/x4iJDPOSv4A + - title: (12:58) Dědičnost - video + url: https://youtu.be/mqxMmSFiASw + + - title: Další odkazy a materiály + +- slug: questions + title: Opakování + description: V této lekci bude prostor zopakovat si vše, co není jasné a odpovědět na vaše dotazy. + date: 2023-01-04 + materials: + + - title: Studijní materiály + + - lesson: adm/refresh + +- slug: pyglet + title: Grafika + date: 2023-01-11 + materials: + + - title: Studijní materiály + + - lesson: intro/pyglet + + - title: "Praktické cvičení: Pong - mimo kurz navíc" + + - lesson: projects/pong + - title: Kód celé hry Pong + url: http://pyladies.cz/v1/s012-pyglet/pong.py + + - title: Další odkazy a materiály + + - title: Tahák na Pyglet + url: https://pyvec.github.io/cheatsheets/pyglet/pyglet-basics-cs.pdf + type: cheatsheet + +- slug: final_projects + title: Závěrečný projekt + date: 2023-01-18 + materials: + + - title: Studijní materiály + + - lesson: projects/asteroids + - lesson: projects/snake + - lesson: klondike/cards + - lesson: klondike/decks + - lesson: klondike/game + + - title: Další odkazy a materiály + + - title: Tahák na geometrii a fyziku 2D her + url: https://pyvec.github.io/cheatsheets/game-physics/game-physics-cs.pdf + type: cheatsheet + + - title: Množinový tahák + url: https://pyvec.github.io/cheatsheets/sets/sets-cs.pdf + type: cheatsheet + +- slug: final_projects_2 + title: Pokračování závěrečného projektu + description: V této lekci budeme pokračovat v dalších fázích závěrečného projektu. + date: 2023-01-25 + +- slug: end + title: Závěrečná hodina + description: Během závěrečné hodiny si předáme diplomy a seznámíme se s možnostmi pokračování studia + date: 2023-02-01 \ No newline at end of file diff --git a/courses/pyladies.yml b/courses/pyladies.yml deleted file mode 100644 index bbd51ab5..00000000 --- a/courses/pyladies.yml +++ /dev/null @@ -1,158 +0,0 @@ -title: Začátečnický kurz -description: Naučte se Python vážně od začátku. Žádné předchozí znalosti nejsou potřeba. -long_description: | - - Zde najdeš materiály, které se používají na začátečnických kurzech PyLadies - v Praze, Brně a Ostravě. - - Jednotlivé lekce jsou určeny naprostým začátečníkům, žádné předchozí - znalosti nejsou nutné. Instrukce jsou uvedeny pro operační systémy Linux, - Windows i macOS. -canonical: true -vars: - coach-present: false -sessions: -- title: Instalace - slug: install - materials: - - lesson: beginners/cmdline - - lesson: beginners/install - - lesson: beginners/venv-setup - - lesson: beginners/first-steps - - lesson: beginners/install-editor - - lesson: git/install - - title: Tahák na klávesnici (PDF) - url: https://pyvec.github.io/cheatsheets/keyboard/keyboard-cs.pdf - type: cheatsheet - -- title: První program - slug: hello - materials: - - lesson: beginners/hello-world - - lesson: beginners/print - - lesson: beginners/variables - - lesson: beginners/comparisons - - lesson: beginners/and-or - -- title: Cykly - slug: loops - materials: - - lesson: beginners/expressions - - lesson: beginners/functions - - lesson: beginners/basic-functions - - lesson: intro/turtle - - lesson: beginners/while - - lesson: beginners/reassignment - - title: Tahák s užitečnými funkcemi - url: https://pyvec.github.io/cheatsheets/basic-functions/basic-functions-cs.pdf - type: cheatsheet - -- title: Správa zdrojového kódu - slug: git - materials: - - lesson: git/basics - - lesson: git/branching - - title: Gitový tahák - url: https://pyvec.github.io/cheatsheets/basic-git/basic-git-cs.pdf - type: cheatsheet - -- title: Řetězce - slug: def-str - materials: - - lesson: beginners/str - - lesson: beginners/str-index-slice - - lesson: beginners/str-methods - - lesson: beginners/fstring - - title: Řetězcový tahák - url: https://pyvec.github.io/cheatsheets/strings/strings-cs.pdf - type: cheatsheet - -- title: Definice funkcí - slug: def - materials: - - lesson: beginners/def - - lesson: beginners/nested-traceback - - lesson: beginners/local-variables - -- title: Chyby a moduly - slug: exc - materials: - - lesson: beginners/exceptions - - lesson: beginners/modules - # XXX when homework is added, include lesson: beginners/circular-imports - - title: Výjimkový tahák - url: https://pyvec.github.io/cheatsheets/exceptions/exceptions-cs.pdf - type: cheatsheet - -- title: Rozhraní a testy - slug: tests - materials: - - lesson: beginners/interfaces - - lesson: beginners/testing - - lesson: beginners/main-module - -- title: Spolupráce a Open-Source - slug: foss - materials: - - lesson: git/collaboration - - lesson: git/ignoring - - title: Gitový tahák - url: https://pyvec.github.io/cheatsheets/basic-git/basic-git-cs.pdf - type: cheatsheet - -- title: Seznamy - slug: list - materials: - - lesson: beginners/list - - lesson: beginners/tuple - - lesson: beginners/nested-list - - title: Tahák na seznamy - url: https://pyvec.github.io/cheatsheets/lists/lists-cs.pdf - type: cheatsheet - -- title: Sekvence a soubory - slug: seq - materials: - - lesson: beginners/range - - lesson: beginners/zip-enumerate - - lesson: beginners/files - -- title: Grafika - slug: pyglet - materials: - - lesson: intro/pyglet - - lesson: projects/pong - - title: Kód celé hry Pong - type: link - url: http://pyladies.cz/v1/s012-pyglet/pong.py - - title: Tahák na Pyglet - url: https://pyvec.github.io/cheatsheets/pyglet/pyglet-basics-cs.pdf - type: cheatsheet - -- title: Slovníky - slug: dict - materials: - - lesson: beginners/dict - - lesson: intro/json - - lesson: projects/github-api - - lesson: beginners/dict-with-list-values - - title: Slovníkový tahák - url: https://pyvec.github.io/cheatsheets/dicts/dicts-cs.pdf - type: cheatsheet - -- title: Třídy - slug: class - materials: - - lesson: beginners/class - - lesson: beginners/inheritance - -- title: Závěrečný projekt - slug: asteroids - materials: - - lesson: projects/asteroids - - title: Množinový tahák - url: https://pyvec.github.io/cheatsheets/sets/sets-cs.pdf - type: cheatsheet - - title: Tahák na geometrii a fyziku 2D her - url: https://pyvec.github.io/cheatsheets/game-physics/game-physics-cs.pdf - type: cheatsheet diff --git a/lessons/adm/intro/index.md b/lessons/adm/intro/index.md new file mode 100644 index 00000000..26714c80 --- /dev/null +++ b/lessons/adm/intro/index.md @@ -0,0 +1,159 @@ +Ahoj! + +vítáme tě v **KURZU PYTHONU 2022 od ostravských PyLadies** !!! + +Níže najdeš důležité informace, které si prosím projdi před první prezenční lekcí. + +#### Kde? +Kurz bude probíhat v budově společnosti TietoEVRY. + +Adresa: Tieto Towers, 28. října 3346/91, Moravská Ostrava, Ostrava + +Mapa: https://mapy.cz/s/cutolecure + +Před první lekcí se všichni potkáme 15 minut před začátkem lekce na recepci v budově Tieto Towers a společně se vydáme do školící místnosti. + +#### Kdy? +Kurz bude probíhat od 12. října 2022 do cca ledna 2023, tedy každou středu v čase od 17:00 do 19:00 hod. + +#### Kolik to stojí? +Kurz nestojí žádné peníze. Ale místo toho stojí čas a úsilí. + +A to jak Tvůj čas, který si musíš vyhradit, aby ses něco naučil/a, +tak čas náš - všech dobrovolníků, kteří kurz dávají dohromady a kteří vám s ním budou pomáhat. + +Prosím, respektuj to. Jestli si nemůžeš vyhradit dost času nebo se tvoje situace změní, dej nám vědět, abychom mohli nabídnout Tvé místo náhradníkovi, který svůj čas investovat může + +#### Jak bude kurz probíhat? +Celý kurz je rozdělen do týdenních lekcí. Všechny materiály budou dopředu k dispozici, takže si je můžeš projít před lekcí. Velká část studijních materiálů bude také doplněna o video, které Tě tématem velmi detailně provede. Každou středu se sejdeme, téma si spolu projdeme a zaměříme se na ty části studijních materiálů, které bude potřeba detailněji vysvětlit a odpovíme na dotazy. + +Každá lekce obsahuje domácí úlohy, na které budeš mít čas do následujícího pondělí. Na prezenčním sektání ve středu si k těmto úlohám řekneme zpětnou vazbu, ukážeme si některá řešení a odpovíme na otázky. + +To, že máme prezenční srazy neznamená, že Ti ve zbytku týdne nepomůžeme. +V průběhu celého kurzu (a i po jeho ukončení) Ti budou k dispozici koučové. +To jsou lidé, kteří prošli kurzem před vámi a nebo už pracují jako programátoři/programátorky. +Pro komunikaci s kouči budeme používat nástroj Slack. +Odkaz na Slack obdržíš emailem na začátku kurzu. + +#### Kolik času to zabere? +Na to, aby ses naučil/a základy programování, budeš potřebovat zhruba tři měsíce. +Každý týden se podíváš na nová videa a ve zbytku týdne si nabyté znalosti budeš zkoušet. +Připrav se na to, že zkoušením strávíš minimálně pět hodin týdně. +Podle toho kolik už znáš to ze začátku může být míň, ale časem určitě přitvrdíme. + +#### Jsou úkoly povinné? +Přihlášeným účastníkům přijde odkaz na takzvané "odevzdávátko", +web kam můžeš po přihlášení nahrát řešení domácích úkolů. +To je povinné. +Ne kvůli nám - když úkoly neodevzdáte, tak nám to, upřímně, jenom ulehčí práci. +Povinné je to kvůli Tobě. +Programovat se nenaučíš jen sledováním videí nebo čtením materiálů, +ale potřebuješ si to sám/sama vyzkoušet. +A kromě toho některé koncepty ve videích vůbec nebudou - budeš na ně v rámci úkolů muset přijít sám/sama, nebo se zeptat. + +Takže dělej úkoly a když něco nebude jasné, tak se ptej. +A když to jasné bude, ale napadne Tě zajímavá otázka, +i kdyby třeba trochu odbíhala od tématu, ptej se taky! + +#### Jak bude probíhat diskuze v průběhu týdne? +Přihlášeným účastníkům přijde odkaz na Slack, kde se rojí jak zkušení programátoři tak nováčci. +Každý v rámci svých možností rád pomůže. + +Tuhle diskusi využívej. +Ptát se je zdaleka to nejužitečnější, co můžeš udělat, aby ses naučil/a programovat. +Dělají to všichni. Programování je tak široký obor, že v něm nikdo neví všechno. +Na všech úrovních je důležité vědět, koho se zeptat a jak se dobře ptát. +A tohle ptaní je jako jakákoli jiná schopnost: jde natrénovat. Takže trénuj. + +Kurz záměrně obsahuje několik míst, která jsou nejasná nebo příliš složitá, +a u kterých se budete muset zeptat. +Můžeš se samozřejmě zeptat zkušených kamarádů nebo zkusit najít odpověď na internetu, +ale nic z toho nepředčí kurzovou diskuzi, kde se všichni snažíme šít odpovědi na míru přímo vám. +Dobře odpovídat je nakonec taky schopnost, a my bychom si ji rádi natrénovali. + +#### Zpětná vazba +Další část ceny za kurz bude zpětná vazba. +Děláme kurzy proto, abychom vylepšil materiály, podle kterých se programování dá naučit. +Na oplátku Tě prosíme, aby ses po každé lekci zamyslel/a a napsal/a, +co se Ti líbilo, co nelíbilo, co jsi pochopil/a a co ještě třeba úplně ne. +Pomůže to kurz dál vylepšovat. + +A samozřejmě, když narazíš na něco, co se dá spravit +hned - třeba chybějící odkaz nebo překlep - tak to nahlas co nejdřív, +nečekej na zpětnou vazbu za celý týden. + +#### Co si z kurzu odnesu? +Budou to základy programování, neboli to, co budeš potřebovat v jakémkoli odvětví, +kde je programování potřeba - ať už se rozhodneš pro datovou analýzu, +automatizaci a správu systémů, vývoj a testování webových nebo grafických aplikací, +strojové učení, robotiku, a tak dále a tak podobně. + +My budeme programovat v jazyce Python, který se učí relativně snadno a dá se i dobře uplatnit v praxi. +Jestli ale pokukuješ po jiném jazyce, nevěš hlavu: koncepty, které se naučíš v Pythonu, +snadno využiješ i v jiných jazycích - ať už je to Rust, C, Java, Javascript, nebo nějaký jiný. +Podobně jako jazyků je hodně oblastí, kde programování využiješ. + +V tomhle kurzu bude většina ukázek ve formě her, které budou (doufejme) aspoň trochu zábavné pro všechny. +Pro většinu z vás budou zároveň nepraktické a možná až infantilní, +ale to nevadí - můžeme Tě ujistit, že koncepty a techniky co se naučíš na hrách, využiješ ve všech ostatních oborech. + +A to mě přivádí k dalšímu nutnému varování: po začátečnickém kurzu z Tebe bude začátečník. +Nebo začátečnice. +Když se budeš dva až tři měsíce snažit, dostaneš dost znalostí a zkušeností na to, +aby Ti dával smysl nějaký pokročilejší kurz - ať už to bude statistika na analýzu dat, +Javascript na weby, nebo informatika na návrh efektivních algoritmů. + +Cíl tohohle kurzu je Ti dát základy, propojit Tě s komunitou a nakonec Tě nasměrovat na další cestu. + +#### Co neočekávat? +Jinými slovy, nečekej, že po tomhle kurzu nastoupíš do nějaké firmy jako programátor. +To je bohužel za tři měsíce dost nerealistické. +Netvrdíme, že to nejde, ale nepočítej s tím. +Pokud práci výhledově chceš, je tenhle kurz důležitý, ale není to jediná věc co budeš potřebovat. +Doporučujeme kouknout na web [junior.guru], kde jsou hezky popsány další kroky. + +Stejně tak nečekej kurz informatiky (angl. computer science), +tedy teoretických a matematických principů, na kterých počítače a programy fungují. +Rozdíl mezi tímhle kurzem a tím, co se učí na vysoké škole, +je zhruba jako mezi autoškolou a vyučením se na automechanika. + +A taky nečekej, že se naučíš dělat webovky nebo trénovat neuronové sítě. +Na obojí existují jiné kurzy, ale u obojího se Ti základy programování budou hodit. + +Tak. Neodradilo tě to? Pojďme si povědět, co budeme očekávat my. + +Kromě času a motivace, což bude to nejdůležitější, budete potřebovat základní vybavení a znalosti. + +[junior.guru]: https://junior.guru/ + +#### Vybavení +Budeš potřebovat vlastní notebook s operačním systémem Linux, Windows nebo macOS, který si přineseš ve středu na lekci. +Také budeš potřebovat mít na tomto notebooku administrátorská prává, aby sis mohl nainstalovat všechny potřebné programy, které budeme používat. + +V rámci domácí přípravy také nedoporučujeme videa sledovat na mobilu nebo na jiné malé obrazovce. +Ve většině videí bude spousta textu střední velikosti. +A ty ho budeš muset číst. + +Nakonec potřebuješ mít přístup k tiskárně. +V průběhu kurzu Vám dáme ke stažení několik taháků, které si můžete vytisknout a mít po ruce. +Nejsme ve škole; není potřeba se znalosti drtit nazpaměť. +Co si nezapamatuješ časem a opakováním, to není důležité nosit v hlavě, tahák nebo internet stačí. + +#### A co znalosti? +Tenhle kurz je pro úplné začátečníky. +Takže lidi, co ještě nikdy nezkoušeli programovat. +Pokud už nějaké kurzy nebo pokusy za sebou máš, budeš se (minimálně ze začátku) nudit. +Zvaž, jestli je to kurz pro Tebe. + +Co potřebovat budeš, jsou základy ovládání počítače. +Potřebuješ umět vytvořit a najít soubor nebo adresář (složku). +Potřebuješ umět nainstalovat nový program. + +A poslední, co budeš potřebovat, jsou základy matematiky ze základky: +třeba co je to dělení se zbytkem nebo obsah čtverce. +Detaily nejsou potřeba, vzorečky se dají najít na Wikipedii. +Spíš potřebuješ vědět, že když máš pokoj tři krát čtyři metry, +tak se tyhle čísla dají nějak zkombinovat a zjistíš výměru podlahy. + +To by bylo vybavení a znalosti, které budeš potřebovat. +Kromě toho budeš mít ještě tři povinnosti: zpětnou vazbu, diskusi a domácí úkoly. \ No newline at end of file diff --git a/lessons/adm/intro/info.yml b/lessons/adm/intro/info.yml new file mode 100644 index 00000000..b3640eaa --- /dev/null +++ b/lessons/adm/intro/info.yml @@ -0,0 +1,4 @@ +title: ⭐️ Úvodní informace ⭐️ +style: md +attribution: Pro týmový kurz PyLadies napsali Petr Viktorin, Adel Popelková a další, 2021. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/adm/refresh/index.md b/lessons/adm/refresh/index.md new file mode 100644 index 00000000..54aba284 --- /dev/null +++ b/lessons/adm/refresh/index.md @@ -0,0 +1,15 @@ + +# Opakovací lekce + +V této lekci si zopakujeme to, co jsme se dosud naučili, vysvětlíme nejasnosti, ukážeme si některá řešení domácích úkolů, odpovíme na vaše dotazy ... + +Můžeme si např. ukázat, kolik je v Pythonu 0,1 + 0,2 nebo jak zapsat v kódu velká čísla ... + +``` +>>> 0.1 + 0.2 == 0.3 +>>> 1_000_000 == 1000000 +``` + +Můžeme si říct něco o pojmech, se kterými se v IT určitě potkáte a ze kterých netřeba mít obavy. Tady náhodný výběr z inzerátů pro (nejen) junior pozice v IT oboru: Git, GitHub, SQL, HTTP, API, REST, JSON, XML, Scrum, Agile, DevOps, CI/CD, IDE, Jira, Postman, SoapUI, HTML, CSS, Docker, Jenkins, Swagger, Bootstrap, Nette, Node.js, OOP, C, C#, C++, .NET, Java, JavaScript, React, BE, FE, UX, QA, QE, ISTQB, Selenium ... + +Nebo i něco jiného ... na co chceš využít tuhle opakovací lekci ty? diff --git a/lessons/adm/refresh/info.yml b/lessons/adm/refresh/info.yml new file mode 100644 index 00000000..3ae24887 --- /dev/null +++ b/lessons/adm/refresh/info.yml @@ -0,0 +1,4 @@ +title: ⭐️ Opakování ⭐️ +style: md +attribution: Pro týmový kurz PyLadies napsala Petra Číhalová, 2022. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/beginners/comparisons/info.yml b/lessons/beginners/comparisons/info.yml index a07649cc..ae9ff3a5 100644 --- a/lessons/beginners/comparisons/info.yml +++ b/lessons/beginners/comparisons/info.yml @@ -1,4 +1,4 @@ -title: Porovnávání +title: Porovnávání a podmínky style: md attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. license: cc-by-sa-40 diff --git a/lessons/beginners/def/index.md b/lessons/beginners/def/index.md index e519d89c..cfe567d9 100644 --- a/lessons/beginners/def/index.md +++ b/lessons/beginners/def/index.md @@ -9,11 +9,11 @@ print('Ahoj světe!') Dnes si ukážeme, jak psát funkce vlastní. -## K čemu jsou funkce? +## K čemu jsou funkce? Často se stává, že kód, který dělá nějakou jednoduchou věc, není úplně jednoduchý. -Jako příklad uvedu nám už známý kód, který v určitém řetězci zamění znak +Jako příklad uvedu nám už známý kód, který v určitém řetězci zamění znak na dané pozici: ```python @@ -22,8 +22,8 @@ konec = slovo[pozice + 1:] nove_slovo = zacatek + novy_znak + konec ``` -Z takového kódu není na první pohled jasné, co přesně dělá. -Zvlášť když kód použiješ ve složitějším programu. +Z takového kódu není na první pohled jasné, co přesně dělá. +Zvlášť když kód použiješ ve složitějším programu. Dá se to vyřešit komentářem: ten, kdo bude program číst, si může přečíst co to má dělat. Samotný složitější kód pak může ignorovat. @@ -82,9 +82,9 @@ Potom následuje odsazené *tělo funkce* – příkazy, které funkce provádí Tělo může začít *dokumentačním řetězcem* (angl. *docstring*), který popisuje co funkce dělá. To může být jakýkoli řetězec, ale tradičně se uvozuje třemi uvozovkami -(i v případě že je jen jednořádkový). +(i v případě že je jen jednořádkový). -Příkazem `return` pak můžeš z funkce *vrátit* nějakou hodnotu. +Příkazem `return` pak můžeš z funkce *vrátit* nějakou hodnotu. Při volání funkce se hodnoty, se kterými funkci zavoláš, přiřadí jednotlivým parametrům. @@ -109,7 +109,7 @@ Aby ho Python vyhodnotil, udělá celý postup výše a jako hodnotu výrazu dos návratovou hodnotu – tedy to, co následuje po `return`. Tělo funkce může mít více příkazů – včetně podmínek, cyklů a podobně. -Následující procedura třeba vypíše skóre daného hráče a k tomu hlášku: +Následující procedura třeba vypíše skóre daného hráče a k tomu hlášku: ```python def napis_hlasku(nazev, skore): @@ -142,6 +142,7 @@ Funkci zavolej a výsledek vypiš. {% filter solution %} ```python def obsah_obdelnika(a, b): + """Vrátí obsah obdélníka s danými stranami.""" return a * b print('Obsah obdélníka se stranami 3 cm a 5 cm je', obsah_obdelnika(3, 5), 'cm2') @@ -152,7 +153,7 @@ print('Obsah obdélníka se stranami 3 cm a 5 cm je', obsah_obdelnika(3, 5), 'cm ## Vracení ukončuje funkci Speciální příkaz `return`, který jde použít jenom ve funkcích, vrátí danou -návratovou hodnotu ven z funkce a zároveň *ukončí* provádění funkce. +návratovou hodnotu ven z funkce a zároveň *ukončí* provádění funkce. Chová se tedy trochu jako `break`, jen místo cyklu opouští celou funkci. @@ -180,8 +181,8 @@ def ano_nebo_ne(otazka): return True elif odpoved == 'ne': return False - - print('Nerozumím! Odpověz "ano" nebo "ne".') + else: + print('Nerozumím! Odpověz "ano" nebo "ne".') # Příklad použití if ano_nebo_ne('Chceš si zahrát hru? '): @@ -193,97 +194,3 @@ else: > [note] > Stejně jako `if` nebo `break` je `return` *příkaz*, ne funkce. > Kolem „své“ hodnoty nepotřebuje závorky. - - -### Vrátit nebo vypsat? - -Podívejme se teď na následující program, který vypíše obsah elipsy: - -```python -from math import pi - -def obsah_elipsy(a, b): - return pi * a * b - -print('Obsah elipsy s poloosami 3 a 5 je', obsah_elipsy(3, 5), 'cm2') -``` - -Takový program se teoreticky dá napsat i s procedurou, tedy funkcí, která nic -nevrací. -Procedura může výsledek třeba vypsat na obrazovku: - -```python -from math import pi - -def obsah_elipsy(a, b): - print('Obsah je', pi * a * b) # Pozor, `print` místo `return`! - -obsah_elipsy(3, 5) -``` - -Program takhle funguje, ale přichází o jednu z hlavních výhod funkcí: -možnost vrácenou hodnotu použít i jinak jež jen v `print`. - -Funkci, která *vrací* výsledek, můžeš použít v dalších výpočtech: - -```python -def objem_eliptickeho_valce(a, b, vyska): - return obsah_elipsy(a, b) * vyska - -print(objem_eliptickeho_valce(3, 5, 3)) -``` - -... ale s procedurou, která výsledek přímo vypíše, by to nešlo. -Proto je dobré psát funkce, které spočítané hodnoty vrací, -a zpracování výsledku (např. vypsání) nechat na kód mimo funkci. - -Další důvod proč hodnoty spíš vracet než vypisovat je ten, že jedna funkce se -dá použít v různých situacích. -Proceduru s `print` by nešlo rozumně použít tehdy, když nás příkazová -řádka vůbec nezajímá – třeba v grafické hře, webové aplikaci, nebo pro ovládání -robota. - -Podobně je to se vstupem: když použiju v rámci své funkce `input`, bude se -moje funkce dát použít jen v situacích, kdy je u počítače klávesnice a za ní -člověk. -Proto je lepší funkcím potřebné informace předávat jako argumenty -a volání `input` (nebo čtení textového políčka či měření čidlem robota) -nemít ve funkci, ale vně, v kódu, který funkci volá: - -```python -from math import pi - -def obsah_elipsy(a, b): - """Vrátí obsah elipsy s poloosami daných délek""" - # Jen samotný výpočet: - return pi * a * b - -# print a input jsou "venku": -x = float(input('Zadej délku poloosy 1: ')) -y = float(input('Zadej délku poloosy 2: ')) -print('Obsah je', obsah_elipsy(x, y)) -``` - -Samozřejmě existují výjimky: procedura, která přímo vytváří textový výpis -(např. tabulku), může používat `print`; funkce, která načítá textové informace -(jako `ano_nebo_ne` výše), zase `input`. -Když ale funkce něco *počítá*, nebo když si nejsi jist{{gnd('ý', 'á')}}, -je dobré ve funkci `print` ani `input` nemít. - - -## None - -Když funkce neskončí příkazem `return`, -automaticky se vrátí hodnota `None`. - -Je to hodnota zabudovaná přímo do Pythonu, podobně jako `True` nebo `False`, -a znamená „nic“. - -```python -def nic(): - """Tahle funkce nic nedělá """ - -print(nic()) -``` - -Procedury v Pythonu vracejí právě toto „nic“. diff --git a/lessons/beginners/prefer-return/index.md b/lessons/beginners/prefer-return/index.md new file mode 100644 index 00000000..183966bb --- /dev/null +++ b/lessons/beginners/prefer-return/index.md @@ -0,0 +1,92 @@ +# Vrátit nebo vypsat? + +Podívejme se teď na následující program, který vypíše obsah elipsy: + +```python +from math import pi + +def obsah_elipsy(a, b): + return pi * a * b + +print('Obsah elipsy s poloosami 3 a 5 je', obsah_elipsy(3, 5), 'cm2') +``` + +Takový program se teoreticky dá napsat i s procedurou, tedy funkcí, která nic +nevrací. +Procedura může výsledek třeba vypsat na obrazovku: + +```python +from math import pi + +def obsah_elipsy(a, b): + print('Obsah je', pi * a * b) # Pozor, `print` místo `return`! + +obsah_elipsy(3, 5) +``` + +Program takhle funguje, ale přichází o jednu z hlavních výhod funkcí: +možnost vrácenou hodnotu použít i jinak jež jen v `print`. + +Funkci, která *vrací* výsledek, můžeš použít v dalších výpočtech: + +```python +def objem_eliptickeho_valce(a, b, vyska): + return obsah_elipsy(a, b) * vyska + +print(objem_eliptickeho_valce(3, 5, 3)) +``` + +... ale s procedurou, která výsledek přímo vypíše, by to nešlo. +Proto je dobré psát funkce, které spočítané hodnoty vrací, +a zpracování výsledku (např. vypsání) nechat na kód mimo funkci. + +Další důvod proč hodnoty spíš vracet než vypisovat je ten, že jedna funkce se +dá použít v různých situacích. +Proceduru s `print` by nešlo rozumně použít tehdy, když nás příkazová +řádka vůbec nezajímá – třeba v grafické hře, webové aplikaci, nebo pro ovládání +robota. + +Podobně je to se vstupem: když použiju v rámci své funkce `input`, bude se +moje funkce dát použít jen v situacích, kdy je u počítače klávesnice a za ní +člověk. +Proto je lepší funkcím potřebné informace předávat jako argumenty +a volání `input` (nebo čtení textového políčka či měření čidlem robota) +nemít ve funkci, ale vně, v kódu, který funkci volá: + +```python +from math import pi + +def obsah_elipsy(a, b): + """Vrátí obsah elipsy s poloosami daných délek""" + # Jen samotný výpočet: + return pi * a * b + +# print a input jsou "venku": +x = float(input('Zadej délku poloosy 1: ')) +y = float(input('Zadej délku poloosy 2: ')) +print('Obsah je', obsah_elipsy(x, y)) +``` + +Samozřejmě existují výjimky: procedura, která přímo vytváří textový výpis +(např. tabulku), může používat `print`; funkce, která načítá textové informace +(jako `ano_nebo_ne` výše), zase `input`. +Když ale funkce něco *počítá*, nebo když si nejsi jist{{gnd('ý', 'á')}}, +je dobré ve funkci `print` ani `input` nemít. + + +## None + +Když funkce neskončí příkazem `return`, +automaticky se vrátí hodnota `None`. + +Je to hodnota zabudovaná přímo do Pythonu, podobně jako `True` nebo `False`, +a znamená „nic“. + +```python +def nic(): + """Tahle funkce nic nedělá """ + +print(nic()) +``` + +Procedury v Pythonu vracejí právě toto „nic“. \ No newline at end of file diff --git a/lessons/beginners/prefer-return/info.yml b/lessons/beginners/prefer-return/info.yml new file mode 100644 index 00000000..453e0e39 --- /dev/null +++ b/lessons/beginners/prefer-return/info.yml @@ -0,0 +1,4 @@ +title: Vrátit nebo vypsat? +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/beginners/vscode/index.md b/lessons/beginners/vscode/index.md new file mode 100644 index 00000000..e9c1e0d8 --- /dev/null +++ b/lessons/beginners/vscode/index.md @@ -0,0 +1,117 @@ +# Visual Studio Code & Python + +Visual Studio Code (také VSCode) je editor zdrojového kódu vyvíjený společností Microsoft pro operační systémy Windows, Linux a macOS. (viz oficiální web: [https://code.visualstudio.com/](https://code.visualstudio.com/) ) + +## Nastavení VSCode + +VSCode umožňuje nastavit spoustu věcí a jelikož pracuji na Windows, budu se bavit hlavně o Windows, ale postupy by měly být totožné i v jiných operačních systémech. + +Jedním ze základním příkazů ve VSCode je otevření příkazové řádky (pozor, jedná se o příkazovou řádku ve VSCode, neplést s příkazovou řádkou ve Winsows). Tato příkazová řádka umožňuje vykonávat různé operace, které jdou vyvolat i jinými způsoby (např. z menu), ale je snazší ji používat, protože umožňuje i textové vyhledávání. Vyvoláme ji jednám z následujících způsobů: + +- z horního menu: `View > Command Pallete...` +- klávesová zkr.: `F1` +- klávesová zkr.: `CTRL+SHIFT+P` (na Macu `CMD+SHIFT+P`) + +a po otevření vypadá následovně + +![Příkazová řádka](./static/cmd-pallete.png) + +### Nastavení terminálu + +VSCode umožňuje nastavit terminál v jakém budeme pracovat, nastavíme jej následovně: + +1. Otevřeme VSCode příkazový řádek +1. Napíšeme `>Terminal: Select Default Profile` +(nebo jen část. Pozor, znak `>` na začátku je důležitý - nemazat) + + ![Select default terminal](./static/cmd-terminal.png) + +1. A vybereme preferovaný terminál - na Windows doporučuju `Command Prompt`, pro jednoduchost a kompatibilitu s kurzy. + + ![CMD](./static/cmd-cmd.png) + +### Instalace rozšíření + +VSCode umožňuje instalovat spoustuužitečných rozšíření a vytvořit tak z jednoduchého textového editoru velmi mocný nástroj. + +Pro naši potřebu budeme potřebovat rozšíření `Python` (od Microsoftu), které nainstalujeme tak, že z levého panelu vybereme ikonu s čtverečky, vyhledávacího pole zadáme `Python` a klikneme na tlačítko `Install`. + +![rozšíření Python](./static/extension.png) + +**Doporučení:** Pokud používáte svůj počítač, máte nainstalováno více rozšíření a víte, že jste je neinstalovali, je dobré je buď zakázat nebo rovnou odstranit - můžou způsobovat nepříjemnosti a zpomalovat celý VSCode. + +## Práce s Pythonem ve VSCode + +### Vytvoření virtuálního prostředí + +1. Otevřete ve VSCode prázdnou složku (nebo složku bez virtuálního prostředí) - složka ve VSCode představuje něco jako projekt, obsahuje zdrojové soubory, ale ukládají se zde i různá nastavení, ... +1. Otevřete `Příkazovou řádku` ve VSCode (kláv. zkr: `F1`) a vyberte příkaz `>Python: Create Environment` (stačí napsat kousek, viz obrázek) a spustí se drobný průvodce, který vám usnadní vytvořit prostředí (viz jednotlivé kroky níže) + + ![Vytvoření prostředí](./static/create-venv.png) + + - Výběr virtuálního prostředí (v kurzech používáme `Venv`) + + ![Výběr typu prostředí](./static/venv-type.png) + + - výběr verze Pythonu (stačí vybrat doporučenou verzi) + + ![Vytvoření prostředí](./static/venv-python.png) + + V mé prázdné složce (`vscode_test`) se vytvoří virtuální prostředí (ve složce `.venv`) + + ![Nové virtuální prostředí](./static/new_venv.png) + +1. Koukneme, jestli se prostředí i aktivovalo... + + - otevřeme si terminál + + - pomocí klávesové zkratky `CTRL+;` (na Macu `CMD+;`) - pomocí této zkratky jde i skrývat a znovu zobrazovat + - z menu `View > Terminal` + + Otevře se nový terminál s aktivovaným prostředím + + ![Nový terminál](./static/terminal.png) + + Pokud chceme terminál odstranit, použijme ikonu koše (okno Terminálu v pravo nahoře) - ikona křížku terminál pouze schová. + + ![Odstranění terminálu](./static/terminal-kill.png) + + - ověřit aktivní prostředí jde i ve spodním panelu, kde se ukáže, jakou verzi pythonu používáme, a že je opravdu z naší složky `.venv` + + ![Ověření prostředí](./static/venv-panel.png) + +### Pytest + +Python rozšíření taky celkem dobře integruje testování do VSCode. Zprovozníme jej následovně: + +1. Otevřeme `VSCode příkazovou řádku` (`F1`) a zadáme příkaz `>Python: Configure Tests` (stčí napsat část, viz obr) + + ![Nastavení Testů](./static/tests-config.png) + +1. Vybereme testovací framework, který chceme použít (v našem případě `pytest`) + + ![Pytest](./static/pytest.png) + +1. Vybert složku, kde jsou (či budou) umístěny testy (v našem případě `.` - hlavní složka projektu). Pytest bude testy hledat pouze v této složce a jejich potomcích. + + ![Nastavení složky s testy](./static/tests-folder.png) + +To je vše - Pytest je nainstalován, nastaven a připraven k použití. VSCode si taky uložilo nějaké nastavení (které jsme mu před chvílí zadali) v naší složce (projektu), které se znova načte při otevření této složky. + + ![Testy: vytvořené testy](./static/tests-settings.png) + +Jak Testy spustit? Jednouše, mámě několik možností + +- přímo z editoru + + ![Testy ve VSCode](./static/pytest-1.png) + +- ze speciálního panelu pro testy + + ![Testy ve VSCode: Ukázka](./static/pytest-2.png) + +Ukázka chyby + + ![Testy ve VSCode: Ukázka](./static/pytest-error.png) + +Poznámka: Samozřejmě můžeme testy pustit i z Terminálu ;-). diff --git a/lessons/beginners/vscode/info.yml b/lessons/beginners/vscode/info.yml new file mode 100644 index 00000000..ad0f9d7d --- /dev/null +++ b/lessons/beginners/vscode/info.yml @@ -0,0 +1,4 @@ +title: VS Code & Python +style: md +attribution: Pro PyLadies Ostrava napsal Michal Vašut, 2022. +license: cc-by-sa-40 diff --git a/lessons/beginners/vscode/static/cmd-cmd.png b/lessons/beginners/vscode/static/cmd-cmd.png new file mode 100644 index 00000000..985ccad5 Binary files /dev/null and b/lessons/beginners/vscode/static/cmd-cmd.png differ diff --git a/lessons/beginners/vscode/static/cmd-pallete.png b/lessons/beginners/vscode/static/cmd-pallete.png new file mode 100644 index 00000000..fcef64a3 Binary files /dev/null and b/lessons/beginners/vscode/static/cmd-pallete.png differ diff --git a/lessons/beginners/vscode/static/cmd-terminal.png b/lessons/beginners/vscode/static/cmd-terminal.png new file mode 100644 index 00000000..8aa75b3a Binary files /dev/null and b/lessons/beginners/vscode/static/cmd-terminal.png differ diff --git a/lessons/beginners/vscode/static/create-venv.png b/lessons/beginners/vscode/static/create-venv.png new file mode 100644 index 00000000..327eb03f Binary files /dev/null and b/lessons/beginners/vscode/static/create-venv.png differ diff --git a/lessons/beginners/vscode/static/extension.png b/lessons/beginners/vscode/static/extension.png new file mode 100644 index 00000000..55982e0c Binary files /dev/null and b/lessons/beginners/vscode/static/extension.png differ diff --git a/lessons/beginners/vscode/static/new_venv.png b/lessons/beginners/vscode/static/new_venv.png new file mode 100644 index 00000000..17d39b13 Binary files /dev/null and b/lessons/beginners/vscode/static/new_venv.png differ diff --git a/lessons/beginners/vscode/static/pytest-1.png b/lessons/beginners/vscode/static/pytest-1.png new file mode 100644 index 00000000..ce1c3b48 Binary files /dev/null and b/lessons/beginners/vscode/static/pytest-1.png differ diff --git a/lessons/beginners/vscode/static/pytest-2.png b/lessons/beginners/vscode/static/pytest-2.png new file mode 100644 index 00000000..c6b73b74 Binary files /dev/null and b/lessons/beginners/vscode/static/pytest-2.png differ diff --git a/lessons/beginners/vscode/static/pytest-error.png b/lessons/beginners/vscode/static/pytest-error.png new file mode 100644 index 00000000..3ff213dd Binary files /dev/null and b/lessons/beginners/vscode/static/pytest-error.png differ diff --git a/lessons/beginners/vscode/static/pytest.png b/lessons/beginners/vscode/static/pytest.png new file mode 100644 index 00000000..18e553cd Binary files /dev/null and b/lessons/beginners/vscode/static/pytest.png differ diff --git a/lessons/beginners/vscode/static/terminal-kill.png b/lessons/beginners/vscode/static/terminal-kill.png new file mode 100644 index 00000000..464f9322 Binary files /dev/null and b/lessons/beginners/vscode/static/terminal-kill.png differ diff --git a/lessons/beginners/vscode/static/terminal.png b/lessons/beginners/vscode/static/terminal.png new file mode 100644 index 00000000..a0717b15 Binary files /dev/null and b/lessons/beginners/vscode/static/terminal.png differ diff --git a/lessons/beginners/vscode/static/tests-config.png b/lessons/beginners/vscode/static/tests-config.png new file mode 100644 index 00000000..d535bfca Binary files /dev/null and b/lessons/beginners/vscode/static/tests-config.png differ diff --git a/lessons/beginners/vscode/static/tests-folder.png b/lessons/beginners/vscode/static/tests-folder.png new file mode 100644 index 00000000..a9bc163f Binary files /dev/null and b/lessons/beginners/vscode/static/tests-folder.png differ diff --git a/lessons/beginners/vscode/static/tests-settings.png b/lessons/beginners/vscode/static/tests-settings.png new file mode 100644 index 00000000..09a11e8f Binary files /dev/null and b/lessons/beginners/vscode/static/tests-settings.png differ diff --git a/lessons/beginners/vscode/static/venv-panel.png b/lessons/beginners/vscode/static/venv-panel.png new file mode 100644 index 00000000..22728e3b Binary files /dev/null and b/lessons/beginners/vscode/static/venv-panel.png differ diff --git a/lessons/beginners/vscode/static/venv-python.png b/lessons/beginners/vscode/static/venv-python.png new file mode 100644 index 00000000..9b870da7 Binary files /dev/null and b/lessons/beginners/vscode/static/venv-python.png differ diff --git a/lessons/beginners/vscode/static/venv-type.png b/lessons/beginners/vscode/static/venv-type.png new file mode 100644 index 00000000..850351f3 Binary files /dev/null and b/lessons/beginners/vscode/static/venv-type.png differ diff --git a/lessons/beginners/while/index.md b/lessons/beginners/while/index.md index 3c7015a1..9983e58d 100644 --- a/lessons/beginners/while/index.md +++ b/lessons/beginners/while/index.md @@ -66,43 +66,3 @@ for i in range(10): # Vnější cyklus break print() ``` - -Ale zpátky k `while`! -Dokážeš napsat tenhle program? - -## Oko bere - -* Začínáš s 0 body. -* Počítač v každém kole vypíše, kolik máš bodů, - a zeptá se tě, jestli chceš pokračovat. -* Pokud odpovíš „ne“, hra končí. -* Pokud odpovíš „ano“, počítač „otočí kartu“ - (náhodně vybere číslo od 2 do 10), vypíše její hodnotu a přičte ji k bodům. -* Pokud máš víc než 21 bodů, prohráváš. -* Cílem hry je získat co nejvíc bodů, ideálně 21. - -{% filter solution %} -```python -from random import randrange - -soucet = 0 -while soucet < 21: - print('Máš', soucet, 'bodů') - odpoved = input('Otočit kartu? ') - if odpoved == 'ano': - karta = randrange(2, 11) - print('Otočil{{a}} jsi', karta) - soucet = soucet + karta - elif odpoved == 'ne': - break - else: - print('Nerozumím! Odpovídej "ano", nebo "ne"') - -if soucet == 21: - print('Gratuluji! Vyhrál{{a}} jsi!') -elif soucet > 21: - print('Smůla!', soucet, 'bodů je moc!') -else: - print('Chybělo jen', 21 - soucet, 'bodů!') -``` -{% endfilter %} diff --git a/lessons/beginners/with/index.md b/lessons/beginners/with/index.md new file mode 100644 index 00000000..a12823ee --- /dev/null +++ b/lessons/beginners/with/index.md @@ -0,0 +1,62 @@ + +# Kontext: `with` a `finally` + +> [note] +> Čteš-li tyto materiály poprvé, tuto sekci můžeš s klidným svědomím přeskočit. +> Pokročilejším ale doporučuju vsadit věci do širšího kontextu. + +Příkaz `with` pracuje s tzv. *kontextem* (tady s kontextem *otevřeného +souboru*), který má začátek a konec a při ukončení je potřeba něco udělat +(tady zavřít soubor). + +Kontext je v podstatě zkratka pro `try`/`finally`. Pamatuješ si na `finally`? + +Toto: + +```python +with open('basnicka.txt', encoding='utf-8') as soubor: + # Zpracování souboru + obsah = soubor.read() +``` + +je zkratka pro: + +```python +soubor = open('basnicka.txt', encoding='utf-8') +try: + # Zpracování souboru + obsah = soubor.read() +finally: + # Ukončení kontextu + soubor.close() +``` + +Jak `with` tak `finally` zaručí, že se soubor vždy uzavře: +když se zpracování povede, ale i když v něm nastane výjimka, +nebo když z něj vyskočíš pomocí `return` nebo `break`: + +```python +def nacti_cele_cislo(jmeno_souboru): + with open(jmeno_souboru, encoding='utf-8') as soubor: + return int(soubor.read()) + # I když "return" vyskočí z funkce (nebo int() zbůsobí ValueError), + # soubor se zavře. + + +# Pro vyzkoušení napiš do souboru 'cislo.txt' nějaké číslo. +print(nacti_cele_cislo('cislo.txt') * 11) +``` + + +## Kontrola výjimek + +Chování příkazu `with` závisí na objektu, se kterým jej použiješ. +Pro soubor – výsledek funkce `open` – se soubor na konci bloku zavře. +Podobných „samozavíracích“ objektů které se dají použít s `with` existuje +v různých knihovnách víc. +Typické jsou objekty, které se starají o připojení např. k jinému počítači +nebo k databázi, kdy je po práci dobré spojení ukončit a „uklidit po sobě“. + +Z lekce o testování si možná pamatuješ `with pytest.raises(...):`. +Výsledek `pytest.raises` na konci bloku `with` kontroluje, že v rámci bloku +nastala správná výjimka. \ No newline at end of file diff --git a/lessons/beginners/with/info.yml b/lessons/beginners/with/info.yml new file mode 100644 index 00000000..8cf0d404 --- /dev/null +++ b/lessons/beginners/with/info.yml @@ -0,0 +1,4 @@ +title: "Kontext: with a finally" +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2021. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/feedback/fb_ov_lesson1/index.ipynb b/lessons/feedback/fb_ov_lesson1/index.ipynb new file mode 100644 index 00000000..35f062ae --- /dev/null +++ b/lessons/feedback/fb_ov_lesson1/index.ipynb @@ -0,0 +1,282 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Zpětná vazba k domácím projektům\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Řetězec x číslo" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "retezec = \"2\"\n", + "cele_cislo = 2\n", + "desetinne_cislo = 2.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Úkol 18 CTRL+C, ^+C" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "KeyboardInterrupt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Úkol 20" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ne všechno se dá použít jako jméno proměnné. Fungují pro proměnné následující jména? Pokud ne, proč asi?\n", + "\n", + "ANO: x, tlacitko4, krůta, kratsiStrana, POCET\\_BODU, \\_ (podtržítko), π (pí), \\_cache, \\_\\_name\\_\\_\n", + "\n", + "NE: 34, 3e4, $i, druha-odmocnina, readme.txt, True, while" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "cannot assign to literal here. Maybe you meant '==' instead of '='? (794101510.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn [2], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m 34 = 'ahoj' # proměnná nemůže být číslo, proměnná nemůže začínat číslem\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to literal here. Maybe you meant '==' instead of '='?\n" + ] + } + ], + "source": [ + "34 = 'ahoj' # proměnná nemůže být číslo, proměnná nemůže začínat číslem" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "30000.0" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3e4" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (3503657673.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn [4], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m $i = 'ahoj' # proměnná nemůže začínat speciálním znakem\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "$i = 'ahoj' # proměnná nemůže začínat speciálním znakem" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "cannot assign to expression here. Maybe you meant '==' instead of '='? (689280700.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn [5], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m druha-odmocnina = 'ahoj' # proměnná nemůže obsahovat pomlčku (pro Python má speciální význam)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to expression here. Maybe you meant '==' instead of '='?\n" + ] + } + ], + "source": [ + "druha-odmocnina = 'ahoj' # proměnná nemůže obsahovat pomlčku (pro Python má speciální význam)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'readme' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn [6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mreadme\u001b[49m\u001b[38;5;241m.\u001b[39mtxt \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mahoj\u001b[39m\u001b[38;5;124m'\u001b[39m\n", + "\u001b[0;31mNameError\u001b[0m: name 'readme' is not defined" + ] + } + ], + "source": [ + "readme.txt = 'ahoj' # proměnná nemůžem obsahovat tečku (pro Python má speciální význam)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "cannot assign to True (3867886315.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn [7], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m True = 'ahoj' # nelze jako proměnnou použít tzv. vyhrazené slovo (reserved word, keyword)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to True\n" + ] + } + ], + "source": [ + "True = 'ahoj' # nelze jako proměnnou použít tzv. vyhrazené slovo (reserved word, keyword)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Základní pravidla pro názvy proměnných\n", + "* libovolná délka\n", + "* case sensitive\n", + "* ano písmena, číslice a podtržítko\n", + "* na začátku nesmí být číslice (nepoužívat na začátku ani podtržíko => speciální význam)\n", + "* názvy proměnných a funkcí malá písmena oddělená podtržítkem (https://peps.python.org/pep-0008/#function-and-variable-names)\n", + "* ne mezera\n", + "* ne vyhrazená slova (https://docs.python.org/3/reference/lexical_analysis.html?highlight=reserved#keywords)\n", + "* ne názvy, které znáte z Pythonu (např. print)\n", + "* ano smysluplné názvy (pozor na jednopísmenné názvy)\n", + "* ne diakritika (lze, ale nepoužívat)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zen of Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "https://cs.wikipedia.org/wiki/Filosofie_Pythonu" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The Zen of Python, by Tim Peters\n", + "\n", + "Beautiful is better than ugly.\n", + "Explicit is better than implicit.\n", + "Simple is better than complex.\n", + "Complex is better than complicated.\n", + "Flat is better than nested.\n", + "Sparse is better than dense.\n", + "Readability counts.\n", + "Special cases aren't special enough to break the rules.\n", + "Although practicality beats purity.\n", + "Errors should never pass silently.\n", + "Unless explicitly silenced.\n", + "In the face of ambiguity, refuse the temptation to guess.\n", + "There should be one-- and preferably only one --obvious way to do it.\n", + "Although that way may not be obvious at first unless you're Dutch.\n", + "Now is better than never.\n", + "Although never is often better than *right* now.\n", + "If the implementation is hard to explain, it's a bad idea.\n", + "If the implementation is easy to explain, it may be a good idea.\n", + "Namespaces are one honking great idea -- let's do more of those!\n" + ] + } + ], + "source": [ + "import this" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Na závěr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Odevzdávat do místa k tomu určenému a ne do komentářů\n", + "* Čeština / Angličtina\n", + "* Dobrá práce !!! 19 z 20 účastníků odevzdalo všechny úkoly\n", + "* Termín pro úkoly?\n", + "* Ptejte se / online konzultace" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lessons/feedback/fb_ov_lesson1/info.yml b/lessons/feedback/fb_ov_lesson1/info.yml new file mode 100644 index 00000000..cbab23fa --- /dev/null +++ b/lessons/feedback/fb_ov_lesson1/info.yml @@ -0,0 +1,4 @@ +title: Zpětná vazba k domácím projektům +style: ipynb +attribution: Pro PyLadies Ostrava napsala Petra Číhalová, 2022. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/feedback/fb_ov_lesson3/index.ipynb b/lessons/feedback/fb_ov_lesson3/index.ipynb new file mode 100644 index 00000000..aab8a671 --- /dev/null +++ b/lessons/feedback/fb_ov_lesson3/index.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Zpětná vazba k domácím projektům\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Nakresli 5, 6, 7, 8 úhelník" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turtle import forward, left, speed, penup, pendown, exitonclick\n", + "n=5\n", + "a= 200/n\n", + "for i in range(5):\n", + " forward(a)\n", + " left(180-(180 * (1-2/n)))\n", + "\n", + "penup()\n", + "forward(a*2)\n", + "pendown()\n", + "\n", + "n=6\n", + "a= 200/n\n", + "for i in range(6):\n", + " forward(a)\n", + " left(180-(180 * (1-2/n)))\n", + " \n", + "penup()\n", + "forward(a*3)\n", + "pendown()\n", + "\n", + "n=7\n", + "a= 200/n\n", + "for i in range(7):\n", + " forward(a)\n", + " left(180-(180 * (1-2/n)))\n", + "\n", + "penup()\n", + "forward(a*3)\n", + "pendown()\n", + "\n", + "n=8\n", + "a= 200/n\n", + "for i in range(8):\n", + " forward(a)\n", + " left(180-(180 * (1-2/n)))\n", + "\n", + "exitonclick()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Jiné řešení" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turtle import exitonclick, forward, left, penup, pendown\n", + "\n", + "for n in range(5,9):\n", + " for i in range(n):\n", + " forward(200/n)\n", + " left(360/n)\n", + " penup()\n", + " forward(90)\n", + " pendown()\n", + "\n", + "exitonclick()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turtle import forward, left, pendown, penup, exitonclick\n", + "\n", + "for n in 5, 6, 7, 8:\n", + " for i in range(n):\n", + " forward(200/n)\n", + " left(360/n)\n", + "\n", + " forward(200/n)\n", + " penup()\n", + " forward(40)\n", + " pendown()\n", + "\n", + "exitonclick()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## range s 1, 2, 3 argumenty" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "for i in range(5):\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "6\n", + "7\n", + "8\n", + "9\n" + ] + } + ], + "source": [ + "for i in range(5, 10):\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "7\n", + "9\n" + ] + } + ], + "source": [ + "for i in range(5, 10, 2):\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "3\n", + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "for i in range(4, -1, -1):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Nejmenší číslo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "zatim_nejmensi = float(input(\"Zadej číslo: \"))\n", + "\n", + "for cislo in range(4):\n", + " nove_cislo = float(input(\"Zadej číslo: \"))\n", + " if nove_cislo < zatim_nejmensi:\n", + " zatim_nejmensi = nove_cislo\n", + " \n", + "print(\"Nejmenší číslo je: \", zatim_nejmensi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Zadej pět čísel a já ti to nejmenší vypíšu.\")\n", + "zatim_nejmensi = None\n", + "\n", + "for cislo in range(5):\n", + " cislo = float(input(\"Zadej libovolné číslo:\"))\n", + "\n", + " if zatim_nejmensi == None or cislo < zatim_nejmensi:\n", + " zatim_nejmensi = cislo\n", + " \n", + "print(\"Nejmenší zadané číslo je:\", zatim_nejmensi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Zadej pět čísel a já ti to nejmenší vypíšu.\")\n", + "\n", + "for i in range(5):\n", + " cislo = float(input(\"Zadej libovolné číslo:\"))\n", + " \n", + " if i == 0 or cislo < zatim_nejmensi:\n", + " zatim_nejmensi = cislo\n", + " \n", + "print(\"Nejmenší zadané číslo je:\", zatim_nejmensi)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Na závěr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Pochvala za úkoly!\n", + "* Pochvala za ptaní!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.8 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "vscode": { + "interpreter": { + "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lessons/feedback/fb_ov_lesson3/info.yml b/lessons/feedback/fb_ov_lesson3/info.yml new file mode 100644 index 00000000..cbab23fa --- /dev/null +++ b/lessons/feedback/fb_ov_lesson3/info.yml @@ -0,0 +1,4 @@ +title: Zpětná vazba k domácím projektům +style: ipynb +attribution: Pro PyLadies Ostrava napsala Petra Číhalová, 2022. +license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/klondike/cards/index.md b/lessons/klondike/cards/index.md new file mode 100644 index 00000000..acdc808c --- /dev/null +++ b/lessons/klondike/cards/index.md @@ -0,0 +1,349 @@ +# Klondike Solitaire: Karty + +Pojďme vytvořit karetní hru *Klondike Solitaire*, kterou možná znáš v nějaké +počítačové verzi. + +{{ figure(img=static('klondike.png'), alt="Jedna z grafických podob hry") }} + +Naše hra bude ze začátku jednodušší – nebudeme se zabývat grafikou, +ale logikou hry. +„Grafiku“ zatím zajistí textová konzole. Obrázek výše se dá ukázat jako: + +```plain + U V W X Y Z + [???] [ ] [ ] [ ] [ ] [ ] + + A B C D E F G + [3♣ ] [???] [???] [???] [???] [???] [???] + [5 ♥] [???] [???] [???] [???] [???] + [6♣ ] [???] [???] [???] [???] + [5♠ ] [???] [???] [???] + [Q ♥] [???] [???] + [4♠ ] [???] + [3 ♦] +``` + + +## Pasiáns + +*Klondike Solitaire* je [pasiáns](https://cs.wikipedia.org/wiki/Pasi%C3%A1ns) +– karetní hra pro jednoho hráče. +Tyto hry obecně fungují takto: + +* Karty se určitým způsobem *rozdají* do několika balíčků, hromádek nebo + jiných skupin +* Dokud hráč *nevyhrál*: + * Hráč *udělá tah*: podle určitých pravidel přesune karty z jedné hromádky + na druhou + +Než ale počítač naučíš hrát hru, je potřeba ho naučit pár základních věcí, +aby pak instrukce pro samotnou hru dávaly smysl. +Základní věci, které je potřeba počítač „naučit“, jsou: + +* Co je to *karta*? +* Co je to *balíček*? + +Odpovědí na tyhle otázky bude spousta vysvětlování a taky několik pythonních +funkcí, které použiješ i ve zbytku hry. + +Tady bych rád podotknul, že tyhle materiály ukazují předem vyzkoušený způsob, +jak napsat karetní hru. +Reálné projekty takhle nefungují: zahrnují spoustu plánování, slepých uliček, +oprav špatně navrženého kódu a jiných frustrací. +Neděláme tu reálný softwarový projekt – zatím stále *zkoušíme základy*, +jen z nich pak vyleze něco hezkého. + + +## Karta a balíček + +Co je to *karta* a *balíček*? +Jak tyhle koncepty *reprezentovat* v Pythonu – tedy pomocí čísel, řetězců, +seznamů, n-tic a jiných datových typů – abys s nimi mohl{{a}} +dál pracovat? + +Možností jak to udělat je více. +Dobrý návrh *datového modelu* je základ úspěšného projektu: odpověď na otázku +výše je základ k tomu, aby se pak program hezky psal. +Až budeš potřebovat dobrý návrh datového modelu pro nějaký svůj projekt, +doporučuju se ze začátku poradit se zkušenějším programátorem. + +Pro Solitaire je tento úkol za tebe vyřešený: hrou Klondike si procvičíš +seznamy a n-tice (a později slovníky). + + +### Karta + +O *kartě* potřebuješ znát tři kousky informace: hodnotu, barvu a to, jestli +je otočená rubem nebo lícem nahoru. + +*Hodnoty* karet jsou čísla 2-10 a navíc `J`, `Q`, `K`, `A`. +Hodnoty „obrázkových“ karet je dobré převést na čísla: J=11, Q=12, K=14, A=1. +Hodnoty se tak budou dát jednoduše porovnávat, nebo zjišťovat následující kartu +(např. po desítce je jedenáct – `J`). +V programu budeme tedy pro hodnoty používat jen čísla, a teprve když bude +potřeba kartu „ukázat“ člověku, převedeme ji na písmenko. + +Pro *barvu* jsou čtyři možnosti: ♥, ♦, ♣ nebo ♠. +Dají se reprezentovat v podstatě jakýmikoli čtyřmi různými hodnotami. +Různí programátoři by mohli použít čísla 0-3, symboly jako `♥`, nebo třeba jako +čtyři různé funkce. +My použijeme krátké řetězce bez diakritiky, aby se to dobře psalo: +`'Sr'` (srdce), `'Pi'` (piky), `'Ka'` (káry), `'Kr'` (kříže). +Použij prosím stejné řetězce (včetně velkých písmen), abys pak mohl{{a}} +kopírovat ukázkový kód. +Jako u hodnoty platí že tyhle řetězce budeme používat ve většině programu, +jen když bude potřeba kartu „ukázat“ člověku, převedeme je na hezčí symbol. + +Pro *otočení* karty jsou dvě možné hodnoty: buď lícem nebo rubem nahoru. +Když dvě hodnoty, je dobré použít `True` a `False`. +Jen je pak potřeba vybrat (a dodržovat) která je která. +Řekněme že `True` znamená lícem nahoru; `False` rubem. +Ideální je podle toho důsledně pojmenovávat proměnné: v programu vždy +používej `je_licem_nahoru=True`, ne `otoceni=True`. + +Samotná karta pak bude trojice těchto hodnot: (hodnota, barva, je_licem_nahoru). +Například: + +* `(12, 'Sr', True)` je 🂽 – srdcová královna otočená lícem nahoru +* `(7, 'Pi', False)` je 🂠 – piková sedma otočená rubem nahoru + + +### Balíček + +A balíček? Balíček bude seznam karet, tedy seznam trojic. +Jakákoli sekvence karet ve hře bude bude seznam trojic: dobírací a odkládací +balíčky, „sloupečky“ karet na herní ploše i „hromádky“ sežazených karet. + +Například jeden ze sloupečků z obrázku výše obsahuje 4 karty rubem nahoru +a na konci srdcovou královnu. +Jako seznam by to mohlo být: + +```python +[(7, 'Pi', False), (5, 'Kr', False), (1, 'Ka', False), (3, 'Pi', False), (12, 'Sr', True)] +``` + + +### Seznamy a n-tice + +Na balíčcích a kartách je vidět rozdíl v použití seznamů a n-tic: + +* N-tice má pevně dané N: karta je trojice, ne čtveřice + ani dvojice. + Oproti tomu seznamy nemívají pevně danou délku: hromádka karet může být velká, + malá, nebo dokonce prázdná. + Dokonce může během hry růst nebo se zmenšovat, třeba když si „lízneš“ kartu + nebo balíček rozdělíš na dvě části. +* Seznamy často dává smysl zamíchat nebo seřadit. + Když zamíchám balíček karet, je to stále baliček karet. + Když ale zamíchám pořadím prvků ve trojici *(hodnota, barva, je_licem_nahoru)*, + bude to sice pořád trojice, ale už to nejspíš nebude *karta*. +* S tím souvisí to, že v seznamy bývají tzv. *homogenní*: každý prvek stejný + typ. Máme balíček karet (trojic), ale karty jsou trojice + (číslo, řetězec, pravdivostní hodnota). + +> [note] +> Ne ve všech programech to bude takhle jednoznačné. Karta a balíček jsou +> skoro ideální příklady na seznamy a n-tice :) + +V Pythonu z použitých typů vyplývá, co se s nimi dá dělat. + +N-tice nejdou měnit: abys změnil{{a}} např. otočení karty, bude +potřeba udělat úplně novou trojici (podobně jako např u tahu +z `--------------------` na `-------------o------` v 1D piškvorkách). + +Seznamy ale měnit jdou. Seznamové operace dokonce často dávají smysl: + +* *append* je přiložení karty na vršek hromádky. +* *pop* je líznutí karty (z balíčku zmizí, ale karta zůstane v ruce – jako návratová hodnota). +* *extend* je přidání jednoho balíčku ke druhému. +* *random.shuffle* je zamíchání karet. +* *sort* je seřazení karet. + +Pozor ale na to, že se seznamem trojic toho jde dělat víc než s fyzickým +balíčkem karet. +Pro počítač není problém udělat kopii karty. + + +## Pomocné funkce + +Označovat dsrdcovou dámu jako `(12, 'Sr', True)` je skvělé pro počítač, +ale pro lidi je to nepřehledné. +Bude tedy vhodné napsat funkci, která kartu „ukáže“ trochu srozumitelněji. +Taková funkce by měla vyřeši i to, že kartu, která je rubem nahoru +– jako `(5, 'Kr', False)`, je potřeba před hráčem skrýt. + +Napsat tuhle funkci je docela otrava, a později bude potřeba +aby se chovala *přesně* podle mých očekávání +(včetně např. velkých písmen a mezer). +Proto ti ji dám k dispozici. Hlavičku má takovouhle: + +```python +def popis_kartu(karta): + """Vrátí popis karty, např. [Q ♥] nebo [6♣ ] nebo [???] + + Trojice čísla (2-13), krátkého řetězce ('Sr', 'Ka', 'Kr' nebo 'Pi') + a logické hodnoty (True - lícem nahoru; False - rubem) se jednoduše + zpracovává v Pythonu, ale pro "uživatele" není nic moc. + Proto je tu tahle funkce, která kartu hezky "popíše". + + Aby měly všechny hodnoty jen jeden znak, desítka se vypisuje jako + římská číslice "X". + + Aby se dalo rychle odlišit červené (♥♦) karty od černých (♣♠), + mají červené mezeru před symbolem a černé za ním. + """ +``` + +Druhá užitečná funkce umí otočit karu buď rubem nebo lícem nahoru. +Podobně jako `tah` z piškvorek vezme „starou“ hodnotu, rozloží ji +části a výsledek slepí z kombinace „starých“ a „nových“ hodnot. + +Projdi si ji řádek po řádku, abys věděl{{a}} jak funguje: + +```python +def otoc_kartu(karta, pozadovane_otoceni): + """Vrátí kartu otočenou lícem nahoru (True) nebo rubem nahoru (False) + + Nemění původní trojici; vytvoří a vrátí novou. + (Ani by to jinak nešlo – n-tice se, podobně jako řetězce čísla, měnit + nedají.) + """ + + # Rozbalení n-tice + hodnota, barva, stare_otoceni = karta + + # Vytvoření nové n-tice (kombinací staré hodnoty/barvy a nového otočení) + nova_karta = hodnota, barva, pozadovane_otoceni + + # Vrácení nové n-tice + return nova_karta +``` + +Funkce najdeš v souboru [`karty.py`]. Projdi si je; rozumíš jim? + +Testy k nim jsou v [`test_karty.py`] – ty procházet nemusíš. + +[`karty.py`]: {{ static('karty.py') }} +[`test_karty.py`]: {{ static('test_karty.py') }} + +Oba soubory si ulož. + + +### Testy a úkoly + +Další pomocné funkce už napíšeš {{gnd('sám', 'sama')}}. +Aby sis ověřil{{a}} že fungují, mám pro tebe předpřipravené testy. + +Stáhni si soubor s testy, [test_klondike.py], a dej ho do adresáře, +kde budeš tvořit hru a kde máš `karty.py`. + +Na ulehčení testování si nainstaluj modul `pytest-level`. +Ten umožňuje pouštět jen určité testy – podle toho, jak jsi daleko. + + python -m pip install pytest pytest-level + +Zkus pustit všechny testy. Asi ti neprojdou: + + python -m pytest -v + +Pak zkus pustit testy pro úroveň 0: + + python -m pytest -v --level 0 + +Teď se nepustí žádné testy – všechny nové testy se přeskočí; +projdou jen testy z `test_karty.py`. +Uvidíš něco jako: + +```pytest +===== 20 passed, 99 deselected in 0.06s ==== +``` + +Zadáš-li v posledním příkazu `--level 1`, aktivuje se první z testů. Pravděpodobně neprojde – v dalším úkolu ho spravíš! + +[test_klondike.py]: {{ static('test_klondike.py') }} + + + +### Vytvoření balíčku + +Do modulu `klondike` (tedy do nového souboru `klondike.py`) napiš +následující funkci: + +```python +def vytvor_balicek(): + """Vrátí balíček 52 karet – od esa (1) po krále (13) ve čtyřech barvách + + Všechny karty jsou otočené rubem nahoru. + """ +``` + +Zkus si funkci pustit a podívej se, co vrací. + +Aby sis ověřil{{a}}, že se chová správně, pusť na ni testy: + +* level 10: Funkce existuje +* level 11: V balíčku je 52 karet, žádné se neopakují. +* level 12: V balíčku jsou všechny požadované karty. +* level 13: Balíček je zamíchaný. + + +### Rozepsání balíčku + +Když výsledek funkce `vytvor_balicek` vypíšeš, je docela nepřehledný. +Aby se ti s balíčky lépe pracovalo, vytvoř následující funkci: + +```python +def popis_balicek(karty): + """Vrátí popis všech karet v balíčku. Jednotlivé karty odděluje mezerami. + """ +``` + +Funkce by měla vracet řetězec složený z popisů jednotlivých karet. +Například: + +```pycon +>>> karty = [ + (13, 'Pi', True), + (12, 'Sr', True), + (11, 'Ka', True), + (10, 'Kr', False), + ] + +>>> popis_balicek(karty) +[K♠ ] [Q ♥] [J ♦] [???] +``` + +Jak na to? +Vytváření celého řetězce najednou by bylo složité, ale lze si to rozdělit +na kroky, které už znáš: + +* Vytvoř prázdný seznam. +* Postupně do senamu přidej popisy jednotlivých karet. + (Využij funkci `popis_kartu` z modulu `karty`!) +* Vrať popisky oddělené mezerami. (Koukni na tahák k seznamům!) + +Funkci opět můžeš otestovat: + +* level 20: Funkce existuje +* level 21: Funkce správně popisuje balíček +* level 22: Funkce umí popsat i prázdný balíček + + +### Popis balíčku + +Občas je z balíčku vidět jen vrchní karta. +Napiš následující funkci, která popíše takový balíček: + +```python +def popis_vrchni_kartu(balicek): + """Vrátí popis daného balíčku karet -- tedy vrchní karty, která je vidět.""" +``` + +Funkci nezapomeň otestovat: + +* level 30: Funkce existuje +* level 31: Funkce vrátí popis poslední karty. (Bude se hodit funkce `popis_kartu` z modulu `karty`.) +* level 32: Funkce popíše prázdný balíček jako `[ ]` (3 mezery v hranatých závorkách). + + +Pokračování příště... diff --git a/lessons/klondike/cards/info.yml b/lessons/klondike/cards/info.yml new file mode 100644 index 00000000..4c830b68 --- /dev/null +++ b/lessons/klondike/cards/info.yml @@ -0,0 +1,4 @@ +title: "Klondike: Karty" +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. +license: cc-by-sa-40 diff --git a/lessons/klondike/cards/static/karty.py b/lessons/klondike/cards/static/karty.py new file mode 100644 index 00000000..cb516666 --- /dev/null +++ b/lessons/klondike/cards/static/karty.py @@ -0,0 +1,69 @@ +"""Základní operace s "kartou" - trojicí (hodnota, barva, je_licem_nahoru) +""" + +def popis_kartu(karta): + """Vrátí popis karty, např. [Q ♥] nebo [6♣ ] nebo [???] + + Trojice čísla (2-13), krátkého řetězce ('Sr', 'Ka', 'Kr' nebo 'Pi') + a logické hodnoty (True - lícem nahoru; False - rubem) se jednoduše + zpracovává v Pythonu, ale pro "uživatele" není nic moc. + Proto je tu tahle funkce, která kartu hezky "popíše". + + Aby měly všechny hodnoty jen jeden znak, desítka se vypisuje jako + římská číslice "X". + + Aby se dalo rychle odlišit červené (♥♦) karty od černých (♣♠), + mají červené mezeru před symbolem a černé za ním. + """ + + # Rozbalení n-tice, abychom mohli pracovat s jednotlivými složkami + hodnota, barva, je_licem_nahoru = karta + + # Když je vidět jen rub, rovnou vrátíme [???] + if not je_licem_nahoru: + return '[???]' + + # Popis hodnoty: pár speciálních případů, plus čísla 2-9 + if hodnota == 11: + popis_hodnoty = 'J' + elif hodnota == 12: + popis_hodnoty = 'Q' + elif hodnota == 13: + popis_hodnoty = 'K' + elif hodnota == 1: + popis_hodnoty = 'A' + elif hodnota == 10: + popis_hodnoty = 'X' + else: + popis_hodnoty = str(hodnota) + + # Popis barvy: čtyři možnosti + if barva == 'Sr': + popis_barvy = ' ♥' + elif barva == 'Ka': + popis_barvy = ' ♦' + elif barva == 'Kr': + popis_barvy = '♣ ' + elif barva == 'Pi': + popis_barvy = '♠ ' + + # Vrácení hodnoty + return f'[{popis_hodnoty}{popis_barvy}]' + + +def otoc_kartu(karta, pozadovane_otoceni): + """Vrátí kartu otočenou lícem nahoru (True) nebo rubem nahoru (False) + + Nemění původní trojici; vytvoří a vrátí novou. + (Ani by to jinak nešlo – n-tice se, podobně jako řetězce čísla, měnit + nedají.) + """ + + # Rozbalení n-tice + hodnota, barva, stare_otoceni = karta + + # Vytvoření nové n-tice (kombinací staré hodnoty/barvy a nového otočení) + nova_karta = hodnota, barva, pozadovane_otoceni + + # Vrácení nové n-tice + return nova_karta diff --git a/lessons/klondike/cards/static/klondike.png b/lessons/klondike/cards/static/klondike.png new file mode 100644 index 00000000..61d321e4 Binary files /dev/null and b/lessons/klondike/cards/static/klondike.png differ diff --git a/lessons/klondike/cards/static/test_karty.py b/lessons/klondike/cards/static/test_karty.py new file mode 100644 index 00000000..aff5a74c --- /dev/null +++ b/lessons/klondike/cards/static/test_karty.py @@ -0,0 +1,70 @@ +import pytest +import karty + + +def test_popis_rubem_nahoru(): + """Popis karty, která je rubem nahoru, by měl ukázat otazníky""" + karta = 13, 'Pi', False + assert karty.popis_kartu(karta) == '[???]' + + +def test_popis_srdcova_kralovna(): + """Popis srdcové královny by měl být "[Q ♥]".""" + karta = 12, 'Sr', True + assert karty.popis_kartu(karta) == '[Q ♥]' + + +def test_popis_krizova_sestka(): + """Popis křížové šestky by měl být "[6♣ ]".""" + karta = 6, 'Kr', True + assert karty.popis_kartu(karta) == '[6♣ ]' + + +def test_popis_karova_desitka(): + """Popis kárové desítky by měl být "[X ♦]".""" + karta = 10, 'Ka', True + assert karty.popis_kartu(karta) == '[X ♦]' + + +def test_popis_pikove_eso(): + """Popis pikového esa by měl být "[A♠ ]".""" + karta = 1, 'Pi', True + assert karty.popis_kartu(karta) == '[A♠ ]' + + +def test_otoc_kralovnu(): + """Kontrola otočení karty, co je na začátku lícem nahoru""" + karta = 12, 'Sr', True + assert karty.otoc_kartu(karta, True) == (12, 'Sr', True) + assert karty.otoc_kartu(karta, False) == (12, 'Sr', False) + + +def test_otoc_eso(): + """Kontrola otočení karty, co je na začátku rubem nahoru""" + karta = 1, 'Pi', False + assert karty.otoc_kartu(karta, True) == (1, 'Pi', True) + assert karty.otoc_kartu(karta, False) == (1, 'Pi', False) + + +# Tohle je testovací vychytávka, kterou zatím neznáme: +# několik podobných testů zadaných jednou funkcí +PRIKLADY = [ + (1, 'Ka', '[A ♦]'), + (2, 'Ka', '[2 ♦]'), + (3, 'Sr', '[3 ♥]'), + (4, 'Sr', '[4 ♥]'), + (5, 'Kr', '[5♣ ]'), + (6, 'Pi', '[6♠ ]'), + (7, 'Ka', '[7 ♦]'), + (8, 'Kr', '[8♣ ]'), + (9, 'Sr', '[9 ♥]'), + (10, 'Kr', '[X♣ ]'), + (11, 'Ka', '[J ♦]'), + (12, 'Sr', '[Q ♥]'), + (13, 'Kr', '[K♣ ]'), +] +@pytest.mark.parametrize(['hodnota', 'barva', 'pozadovany_popis'], PRIKLADY) +def test_popis_hodnoty(hodnota, barva, pozadovany_popis): + """Kontrola popisu karty""" + karta = hodnota, barva, True + assert karty.popis_kartu(karta) == pozadovany_popis diff --git a/lessons/klondike/cards/static/test_klondike.py b/lessons/klondike/cards/static/test_klondike.py new file mode 100644 index 00000000..731d11db --- /dev/null +++ b/lessons/klondike/cards/static/test_klondike.py @@ -0,0 +1,824 @@ +import pytest +import textwrap +import re + +@pytest.mark.level(1) +def test_import_klondike(): + import klondike + + +@pytest.mark.level(10) +def test_import_vytvor_balicek(): + from klondike import vytvor_balicek + + +@pytest.mark.level(11) +def test_vytvor_balicek_52(): + """Balíček by měl obsahovat 52 karet""" + from klondike import vytvor_balicek + assert len(vytvor_balicek()) == 52 + + +@pytest.mark.level(11) +def test_vytvor_balicek_bez_duplikatu(): + """Balíček by neměl obsahovat duplikáty""" + from klondike import vytvor_balicek + balicek = vytvor_balicek() + for karta in balicek: + assert balicek.count(karta) == 1 + + +@pytest.mark.level(12) +@pytest.mark.parametrize('hodnota', range(2, 14)) +def test_vytvor_balicek_pocet_hodnoty(hodnota): + """Balíček by měl obsahovat 4 karty dané hodnoty""" + from klondike import vytvor_balicek + balicek = vytvor_balicek() + pocet = 0 + for hodnota_karty, barva_karty, je_licem_nahoru in balicek: + if hodnota_karty == hodnota: + pocet = pocet + 1 + assert pocet == 4 + + +@pytest.mark.level(12) +@pytest.mark.parametrize('barva', ['Pi', 'Sr', 'Ka', 'Kr']) +def test_vytvor_balicek_pocet_barvy(barva): + """Balíček by měl obsahovat 13 karet dané barvy""" + from klondike import vytvor_balicek + balicek = vytvor_balicek() + pocet = 0 + for hodnota_karty, barva_karty, je_licem_nahoru in balicek: + if barva_karty == barva: + pocet = pocet + 1 + assert pocet == 13 + + +@pytest.mark.level(13) +def test_zamichani_balicku(): + """Každá hra by měla být jiná""" + from klondike import vytvor_balicek + balicek1 = vytvor_balicek() + balicek2 = vytvor_balicek() + + # Je šance 1 z 80658175170943878571660636856403766975289505440883277824000000000000, + # že dva náhodné balíčky budou stejné. + # Nejspíš je pravděpodobnější, že v průběhu testu odejde počítač, + # na kterém test běží, než aby se ty karty zamíchaly stejně. + assert balicek1 != balicek2, 'Karty nejsou zamíchané!' + +@pytest.mark.level(20) +def test_import_popis_popis_balicek(): + from klondike import popis_balicek + + +@pytest.mark.level(21) +def test_popis_balicek(): + from klondike import popis_balicek + karty = [ + (13, 'Pi', True), + (12, 'Sr', True), + (11, 'Ka', True), + (10, 'Kr', False) + ] + assert popis_balicek(karty) == '[K♠ ] [Q ♥] [J ♦] [???]' + + +@pytest.mark.level(22) +def test_popis_prazdny_balicek(): + from klondike import popis_balicek + assert popis_balicek([]) == '' + + +@pytest.mark.level(30) +def test_import_popis_vrchni_kartu(): + from klondike import popis_vrchni_kartu + + +@pytest.mark.level(31) +def test_popis_vrchni_kartu_jedna_karta(): + """Balíček se srdcovou dámou by se měl popsat jako tato karta""" + from klondike import popis_vrchni_kartu + karta = 12, 'Sr', True + assert popis_vrchni_kartu([karta]) == '[Q ♥]' + + +@pytest.mark.level(31) +def test_popis_vrchni_kartu_moc_karet(): + """Balíček se víc kartama by se měl popsat jako vrchní karta""" + from klondike import popis_vrchni_kartu + rubem_nahoru = 1, 'Sr', False + karta = 12, 'Sr', True + balicek = [rubem_nahoru, rubem_nahoru, rubem_nahoru, karta] + assert popis_vrchni_kartu(balicek) == '[Q ♥]' + + +@pytest.mark.level(31) +def test_popis_vrchni_kartu_rubem_nahoru(): + """Balíček s vrchní kartou rubem nahoru by se měl popsat jako [???]""" + from klondike import popis_vrchni_kartu + rubem_nahoru = 1, 'Sr', False + karta = 12, 'Sr', True + balicek = [karta, karta, karta, rubem_nahoru] + assert popis_vrchni_kartu(balicek) == '[???]' + +@pytest.mark.level(32) +def test_popis_vrchni_kartu_prazdneho_balicku(): + """Prázdný balíček se popisuje pomocí [ ]""" + from klondike import popis_vrchni_kartu + assert popis_vrchni_kartu([]) == '[ ]' + + +@pytest.mark.level(40) +def test_import_rozdej_sloupecky(): + from klondike import rozdej_sloupecky + + +@pytest.mark.level(41) +def test_rozdej_sloupecky_7(): + """Rozdaných sloupečků má být 7""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + assert len(sloupecky) == 7 + + +@pytest.mark.level(41) +def test_rozdej_sloupecky_velikost_balicku(): + """V balíčku by měly chybět karty ze sloupečků""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + + # Ceklový počet karet ve sloupečcích + velikost_vsech_sloupecku = 0 + for sloupecek in sloupecky: + velikost_vsech_sloupecku = velikost_vsech_sloupecku + len(sloupecek) + + # Kontrola počtu karet v balíčku + assert len(balicek) == 52 - velikost_vsech_sloupecku + + +@pytest.mark.level(41) +def test_rozdej_sloupecky_zvrchu_balicku(): + """Karty by měly být rozdané z konce balíčku""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + kopie_puvodniho_balicku = list(balicek) + sloupecky = rozdej_sloupecky(balicek) + + assert balicek == kopie_puvodniho_balicku[:len(balicek)] + + +@pytest.mark.level(41) +def test_rozdej_sloupecky_nechybi_karty(): + """V balíčku a sloupečcích by měly být všechny karty""" + from klondike import vytvor_balicek, rozdej_sloupecky + from karty import otoc_kartu + balicek = vytvor_balicek() + kopie_puvodniho_balicku = list(balicek) + sloupecky = rozdej_sloupecky(balicek) + + vsechny_karty = list(balicek) + for sloupecek in sloupecky: + for karta in sloupecek: + vsechny_karty.append(otoc_kartu(karta, False)) + + vsechny_karty.sort() + kopie_puvodniho_balicku.sort() + + assert vsechny_karty == kopie_puvodniho_balicku + + +@pytest.mark.level(41) +def test_rozdej_sloupecky_balicek_rubem_nahoru(): + """Po rozdání sloupečků by měl celý balíček být rubem nahoru""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + + for hodnota, barva, je_licem_nahoru in balicek: + assert not je_licem_nahoru + + +@pytest.mark.level(42) +@pytest.mark.parametrize('cislo_sloupce', range(7)) +def test_rozdej_sloupecky_posledni_licem_nahoru(cislo_sloupce): + """Poslední karta sloupečku je lícem nahoru""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + posledni_karta = sloupecky[cislo_sloupce][-1] + hodnota, barva, je_licem_nahoru = posledni_karta + assert je_licem_nahoru + + +@pytest.mark.level(42) +@pytest.mark.parametrize('cislo_sloupce', range(7)) +def test_rozdej_sloupecky_ostatni_rubem_nahoru(cislo_sloupce): + """Karty pod první kartou sloupečku jsou rubem nahoru""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + for karta in sloupecky[cislo_sloupce][:-1]: + hodnota, barva, je_licem_nahoru = karta + assert not je_licem_nahoru + + +@pytest.mark.level(43) +@pytest.mark.parametrize('cislo_sloupce', range(7)) +def test_rozdej_sloupecky_velikost(cislo_sloupce): + """Kontrola velikosti rozdaného sloupečku""" + from klondike import vytvor_balicek, rozdej_sloupecky + balicek = vytvor_balicek() + sloupecky = rozdej_sloupecky(balicek) + assert len(sloupecky[cislo_sloupce]) == cislo_sloupce + 1 + + +@pytest.mark.level(50) +def test_import_vypis_sloupecky(): + from klondike import vypis_sloupecky + + +def check_text(got, expected): + got = re.sub(' +\n', '\n', got) # odstraní mezery z konců řádků + print(got) + assert got.strip() == textwrap.dedent(expected).strip() + + +@pytest.mark.level(51) +def test_vypis_prazdne_sloupecky(capsys): + from klondike import vypis_sloupecky + vypis_sloupecky([[], [], [], [], [], [], [], []]) + out, err = capsys.readouterr() + check_text(out, "") + + +@pytest.mark.level(51) +def test_vypis_sloupecky_jedna_karta_rubem_nahoru(capsys): + from klondike import vypis_sloupecky + vypis_sloupecky([[(1, 'Pi', False)]] * 7) + out, err = capsys.readouterr() + check_text(out, "[???] [???] [???] [???] [???] [???] [???]") + + +@pytest.mark.level(51) +def test_vypis_sloupecky_po_jedne_karte_licem_nahoru(capsys): + from klondike import vypis_sloupecky + vypis_sloupecky([ + [(1, 'Pi', True)], + [(2, 'Sr', True)], + [(3, 'Ka', True)], + [(4, 'Kr', True)], + [(5, 'Pi', True)], + [(6, 'Sr', True)], + [(7, 'Ka', True)], + ]) + out, err = capsys.readouterr() + check_text(out, "[A♠ ] [2 ♥] [3 ♦] [4♣ ] [5♠ ] [6 ♥] [7 ♦]") + + +@pytest.mark.level(52) +def test_vypis_sloupecky_dvou_kartach(capsys): + from klondike import vypis_sloupecky + vypis_sloupecky([ + [(1, 'Pi', True), (7, 'Sr', True)], + [(2, 'Sr', True), (6, 'Ka', True)], + [(3, 'Ka', True), (5, 'Kr', False)], + [(4, 'Kr', False), (4, 'Pi', True)], + [(5, 'Pi', False), (3, 'Sr', True)], + [(6, 'Sr', True), (2, 'Ka', True)], + [(7, 'Ka', True), (1, 'Kr', True)], + ]) + out, err = capsys.readouterr() + check_text(out, """ + [A♠ ] [2 ♥] [3 ♦] [???] [???] [6 ♥] [7 ♦] + [7 ♥] [6 ♦] [???] [4♠ ] [3 ♥] [2 ♦] [A♣ ] + """) + + +@pytest.mark.level(52) +def test_vypis_sloupecky_vice_karet(capsys): + from klondike import vypis_sloupecky + vypis_sloupecky([ + [(1, 'Pi', True)], + [(2, 'Pi', True), (2, 'Sr', True)], + [(3, 'Pi', True), (3, 'Sr', True), (3, 'Ka', True)], + [(4, 'Pi', True), (4, 'Sr', True), (4, 'Ka', False), (4, 'Kr', True)], + [(5, 'Pi', True), (5, 'Sr', True), (5, 'Ka', True)], + [(6, 'Pi', True), (6, 'Sr', True)], + [(7, 'Pi', True)], + ]) + out, err = capsys.readouterr() + check_text(out, """ + [A♠ ] [2♠ ] [3♠ ] [4♠ ] [5♠ ] [6♠ ] [7♠ ] + [2 ♥] [3 ♥] [4 ♥] [5 ♥] [6 ♥] + [3 ♦] [???] [5 ♦] + [4♣ ] + """) + + +@pytest.mark.level(52) +def test_vypis_sloupecky_ruby(capsys): + """Kontrola výpisu sloupečků, kde jsou všechny karty rubem nahoru""" + from klondike import vypis_sloupecky + sloupecky = [ + [(13, 'Pi', False)] * 2, + [(13, 'Pi', False)] * 3, + [(13, 'Pi', False)] * 4, + [(13, 'Pi', False)] * 5, + [(13, 'Pi', False)] * 6, + [(13, 'Pi', False)] * 7, + [(13, 'Pi', False)] * 8, + ] + vypis_sloupecky(sloupecky) + out, err = capsys.readouterr() + check_text(out, """ + [???] [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] + [???] [???] [???] [???] + [???] [???] [???] + [???] [???] + [???] + """) + + +@pytest.mark.level(52) +def test_vypis_sloupecky_zacatek_hry(capsys): + """Kontrola výpisu sloupečků, kde jsou karty i rubem lícem nahoru""" + from klondike import vypis_sloupecky + sloupecky = [ + [(13, 'Pi', False)] * 0 + [(8, 'Kr', True)], + [(13, 'Pi', False)] * 1 + [(9, 'Ka', True)], + [(13, 'Pi', False)] * 2 + [(10, 'Sr', True)], + [(13, 'Pi', False)] * 3 + [(1, 'Ka', True)], + [(13, 'Pi', False)] * 4 + [(4, 'Pi', True)], + [(13, 'Pi', False)] * 5 + [(9, 'Kr', True)], + [(13, 'Pi', False)] * 6 + [(12, 'Sr', True)], + ] + vypis_sloupecky(sloupecky) + out, err = capsys.readouterr() + check_text(out, """ + [8♣ ] [???] [???] [???] [???] [???] [???] + [9 ♦] [???] [???] [???] [???] [???] + [X ♥] [???] [???] [???] [???] + [A ♦] [???] [???] [???] + [4♠ ] [???] [???] + [9♣ ] [???] + [Q ♥] + """) + + +@pytest.mark.level(52) +def test_vypis_sloupecky_rozehrana(capsys): + """Kontrola výpisu sloupečků rozehrané hry""" + from klondike import vypis_sloupecky + sloupecky = [ + [(13, 'Pi', False)] * 1 + [(8, 'Kr', True)], + [(13, 'Pi', False)] * 8 + [(9, 'Ka', True)], + [(13, 'Pi', False)] * 2 + [(10, 'Sr', True), (9, 'Kr', True), (8, 'Ka', True)], + [(13, 'Pi', False)] * 6 + [(3, 'Ka', True)], + [(13, 'Pi', False)] * 1 + [(4, 'Pi', True)], + [(13, 'Pi', False)] * 9 + [(9, 'Kr', True)], + [(13, 'Pi', False)] * 5 + [(12, 'Sr', True), (11, 'Pi', True)], + ] + vypis_sloupecky(sloupecky) + out, err = capsys.readouterr() + check_text(out, """ + [???] [???] [???] [???] [???] [???] [???] + [8♣ ] [???] [???] [???] [4♠ ] [???] [???] + [???] [X ♥] [???] [???] [???] + [???] [9♣ ] [???] [???] [???] + [???] [8 ♦] [???] [???] [???] + [???] [???] [???] [Q ♥] + [???] [3 ♦] [???] [J♠ ] + [???] [???] + [9 ♦] [???] + [9♣ ] + """) + + +@pytest.mark.level(60) +def test_import_presun_kartu(): + from klondike import presun_kartu + + +@pytest.mark.level(61) +def test_presun_kartu_licem_nahoru(): + """Kontrola přesunutí karty, co je na začátku lícem nahoru""" + from klondike import presun_kartu + zdroj = [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + ] + cil = [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + ] + presun_kartu(zdroj, cil, True) + assert zdroj == [ + (3, 'Kr', False), + (4, 'Sr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + (5, 'Kr', True), + ] + presun_kartu(zdroj, cil, False) + assert zdroj == [ + (3, 'Kr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + (5, 'Kr', True), + (4, 'Sr', False), + ] + + +@pytest.mark.level(61) +def test_presun_kartu_rubem_nahoru(): + """Kontrola přesunutí karty, co je na začátku rubem nahoru""" + from klondike import presun_kartu + zdroj = [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + ] + cil = [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + ] + presun_kartu(zdroj, cil, True) + assert zdroj == [ + (11, 'Pi', True), + (12, 'Ka', True), + ] + assert cil == [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + (13, 'Pi', True), + ] + presun_kartu(zdroj, cil, False) + assert zdroj == [ + (11, 'Pi', True), + ] + assert cil == [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + (13, 'Pi', True), + (12, 'Ka', False), + ] + + +@pytest.mark.level(70) +def test_import_presun_nekolik_karet(): + from klondike import presun_nekolik_karet + + +@pytest.mark.level(71) +def test_presun_jednu_kartu(): + """Zkontroluje přesunutí jedné karty pomocí presun_nekolik_karet""" + from klondike import presun_nekolik_karet + zdroj = [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + ] + cil = [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + ] + presun_nekolik_karet(zdroj, cil, 1) + assert zdroj == [ + (3, 'Kr', False), + (4, 'Sr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + (5, 'Kr', False), + ] + + +@pytest.mark.level(71) +def test_presun_dve_karty(): + """Zkontroluje přesunutí dvou karet najednou""" + from klondike import presun_nekolik_karet + zdroj = [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + ] + cil = [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + ] + presun_nekolik_karet(zdroj, cil, 2) + assert zdroj == [ + (3, 'Kr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + (4, 'Sr', False), + (5, 'Kr', False), + ] + + +@pytest.mark.level(71) +def test_presun_tam_a_zpet(): + """Zkontroluje přesouvání karet tam a zpátky""" + from klondike import presun_nekolik_karet + zdroj = [ + (3, 'Kr', False), + (4, 'Sr', False), + (5, 'Kr', False), + ] + cil = [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + ] + presun_nekolik_karet(zdroj, cil, 1) + assert zdroj == [ + (3, 'Kr', False), + (4, 'Sr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (13, 'Pi', True), + (5, 'Kr', False), + ] + presun_nekolik_karet(cil, zdroj, 2) + assert zdroj == [ + (3, 'Kr', False), + (4, 'Sr', False), + (13, 'Pi', True), + (5, 'Kr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + ] + presun_nekolik_karet(zdroj, cil, 3) + assert zdroj == [ + (3, 'Kr', False), + ] + assert cil == [ + (11, 'Pi', True), + (12, 'Ka', True), + (4, 'Sr', False), + (13, 'Pi', True), + (5, 'Kr', False), + ] + presun_nekolik_karet(cil, zdroj, 4) + assert zdroj == [ + (3, 'Kr', False), + (12, 'Ka', True), + (4, 'Sr', False), + (13, 'Pi', True), + (5, 'Kr', False), + ] + assert cil == [ + (11, 'Pi', True), + ] + presun_nekolik_karet(zdroj, cil, 5) + assert zdroj == [ + ] + assert cil == [ + (11, 'Pi', True), + (3, 'Kr', False), + (12, 'Ka', True), + (4, 'Sr', False), + (13, 'Pi', True), + (5, 'Kr', False), + ] + + +@pytest.mark.level(80) +def test_import_udelej_hru(): + from klondike import udelej_hru + +@pytest.mark.level(81) +def test_udelej_hru_klice(): + """Hra by měl být slovník s klíči A až G a U až Z.""" + from klondike import udelej_hru + hra = udelej_hru() + assert sorted(hra) == list('ABCDEFGUVWXYZ') + + +@pytest.mark.level(81) +@pytest.mark.parametrize('pismenko', 'ABCDEFGUVWXYZ') +def test_pocty_karet(pismenko): + """Počty karet v jednotlivých sloupcích jsou dané.""" + from klondike import udelej_hru + hra = udelej_hru() + + POCTY = { + 'U': 24, + 'V': 0, 'W': 0, 'X': 0, 'Y': 0, 'Z': 0, + 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, + } + pozadovany_pocet = POCTY[pismenko] + + assert len(hra[pismenko]) == pozadovany_pocet + + +@pytest.mark.level(81) +def test_otoceni_karet_balicku(): + """Karty balíčku by měly být rubem nahoru""" + from klondike import udelej_hru + hra = udelej_hru() + for hodnota, barva, licem_nahoru in hra['U']: + assert not licem_nahoru + + +@pytest.mark.level(81) +@pytest.mark.parametrize('pismenko', 'ABCDEFG') +def test_otoceni_karet_sloupecku(pismenko): + """Karty sloupečků by měly být rubem nahoru, kromě té poslední""" + from klondike import udelej_hru + hra = udelej_hru() + sloupecek = hra[pismenko] + + # Poslední karta + posledni_karta = sloupecek[-1] + hodnota, barva, licem_nahoru = posledni_karta + assert licem_nahoru + + # Ostatní karty + for hodnota, barva, licem_nahoru in sloupecek[:-1]: + assert not licem_nahoru + + +@pytest.mark.level(81) +def test_zamichani_hry(): + """Každá hra by měla být jiná""" + from klondike import udelej_hru + hra1 = udelej_hru() + hra2 = udelej_hru() + + # Je šance 1 z 80658175170943878571660636856403766975289505440883277824000000000000, + # že dvě náhodné hry budou stejné. + # Nejspíš je pravděpodobnější, že v průběhu testu odejde počítač, + # na kterém test běží, než aby se ty karty zamíchaly stejně. + assert hra1 != hra2, 'Karty nejsou zamíchané!' + + +@pytest.mark.level(81) +def test_vsech_karet(): + """Hra by měla obsahovat všech 52 karet, bez duplikátů.""" + from klondike import udelej_hru + hra = udelej_hru() + + # Uděláme seznam dvojic (hodnota, barva), tedy karet s ignorovaným otočením + dvojice_z_hry = [] + for balicek in hra.values(): + for hodnota, barva, licem_nahoru in balicek: + dvojice_z_hry.append((hodnota, barva)) + # Seznam seřadíme -- na pořadí nezáleží + dvojice_z_hry.sort() + + # Uděláme seznam dvojic (hodnota, barva) všech karet, kteŕe ve hře mají být + pozadovane_dvojice = [] + for hodnota in range(1, 14): + for barva in 'Ka', 'Kr', 'Pi', 'Sr': + pozadovane_dvojice.append((hodnota, barva)) + # Tenhle seznam by měl být už seřazený, ale opatrnosti není nikdy dost + pozadovane_dvojice.sort() + + # Ty dva seznamy (ten ze hry a ten z testu) by měly být stejné + assert dvojice_z_hry == pozadovane_dvojice + + +@pytest.mark.level(90) +def test_import_vypis_hru(): + from klondike import vypis_hru + +@pytest.mark.level(91) +def test_ruby(capsys): + """Kontrola výpisu hry, kde jsou všechny karty rubem nahoru""" + from klondike import udelej_hru, vypis_hru + hra = udelej_hru() + hra = { + 'U': [(13, 'Pi', False)], + 'V': [], + 'W': [], + 'X': [], + 'Y': [], + 'Z': [], + 'A': [(13, 'Pi', False)] * 2, + 'B': [(13, 'Pi', False)] * 3, + 'C': [(13, 'Pi', False)] * 4, + 'D': [(13, 'Pi', False)] * 5, + 'E': [(13, 'Pi', False)] * 6, + 'F': [(13, 'Pi', False)] * 7, + 'G': [(13, 'Pi', False)] * 8, + } + vypis_hru(hra) + out, err = capsys.readouterr() + check_text(out, """ + U V W X Y Z + [???] [ ] [ ] [ ] [ ] [ ] + + A B C D E F G + [???] [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] [???] + [???] [???] [???] [???] [???] + [???] [???] [???] [???] + [???] [???] [???] + [???] [???] + [???] + """) + + +@pytest.mark.level(91) +def test_zacatek_hry(capsys): + """Kontrola výpisu hry, kde jsou karty i rubem lícem nahoru""" + from klondike import vypis_hru + hra = { + 'U': [(13, 'Pi', False)], + 'V': [(8, 'Kr', True), (13, 'Pi', True)], + 'W': [], + 'X': [], + 'Y': [], + 'Z': [], + 'A': [(13, 'Pi', False)] * 0 + [(8, 'Kr', True)], + 'B': [(13, 'Pi', False)] * 1 + [(9, 'Ka', True)], + 'C': [(13, 'Pi', False)] * 2 + [(10, 'Sr', True)], + 'D': [(13, 'Pi', False)] * 3 + [(1, 'Ka', True)], + 'E': [(13, 'Pi', False)] * 4 + [(4, 'Pi', True)], + 'F': [(13, 'Pi', False)] * 5 + [(9, 'Kr', True)], + 'G': [(13, 'Pi', False)] * 6 + [(12, 'Sr', True)], + } + vypis_hru(hra) + out, err = capsys.readouterr() + check_text(out, """ + U V W X Y Z + [???] [K♠ ] [ ] [ ] [ ] [ ] + + A B C D E F G + [8♣ ] [???] [???] [???] [???] [???] [???] + [9 ♦] [???] [???] [???] [???] [???] + [X ♥] [???] [???] [???] [???] + [A ♦] [???] [???] [???] + [4♠ ] [???] [???] + [9♣ ] [???] + [Q ♥] + """) + + +@pytest.mark.level(91) +def test_rozehrana(capsys): + from klondike import vypis_hru + """Kontrola výpisu rozehrané hry""" + hra = { + 'U': [(13, 'Pi', False)], + 'V': [(8, 'Kr', True), (13, 'Pi', True)], + 'W': [(1, 'Pi', True)], + 'X': [(1, 'Kr', True)], + 'Y': [(1, 'Sr', True)], + 'Z': [(1, 'Ka', True), (2, 'Ka', True)], + 'A': [(13, 'Pi', False)] * 1 + [(8, 'Kr', True)], + 'B': [(13, 'Pi', False)] * 8 + [(9, 'Ka', True)], + 'C': [(13, 'Pi', False)] * 2 + [(10, 'Sr', True), (9, 'Kr', True), (8, 'Ka', True)], + 'D': [(13, 'Pi', False)] * 6 + [(3, 'Ka', True)], + 'E': [(13, 'Pi', False)] * 1 + [(4, 'Pi', True)], + 'F': [(13, 'Pi', False)] * 9 + [(9, 'Kr', True)], + 'G': [(13, 'Pi', False)] * 5 + [(12, 'Sr', True), (11, 'Pi', True)], + } + vypis_hru(hra) + out, err = capsys.readouterr() + check_text(out, """ + U V W X Y Z + [???] [K♠ ] [A♠ ] [A♣ ] [A ♥] [2 ♦] + + A B C D E F G + [???] [???] [???] [???] [???] [???] [???] + [8♣ ] [???] [???] [???] [4♠ ] [???] [???] + [???] [X ♥] [???] [???] [???] + [???] [9♣ ] [???] [???] [???] + [???] [8 ♦] [???] [???] [???] + [???] [???] [???] [Q ♥] + [???] [3 ♦] [???] [J♠ ] + [???] [???] + [9 ♦] [???] + [9♣ ] + """) diff --git a/lessons/klondike/decks/index.md b/lessons/klondike/decks/index.md new file mode 100644 index 00000000..7d5d8683 --- /dev/null +++ b/lessons/klondike/decks/index.md @@ -0,0 +1,223 @@ +# Klondike Solitaire: Balíčky + +Postupně tvoříme hru *Klondike Solitaire*, která bude nakonec fungovat takto: + +* Karty se určitým způsobem *rozdají* do několika balíčků, hromádek nebo + jiných skupin +* Dokud hráč *nevyhrál*: + * Hráč *udělá tah*: podle určitých pravidel přesune karty z jedné hromádky + na druhou + +Pro počítačovou verzi to bude potřeba doplnit o zobrazení stavu hry +a o načítání hráčova tahu: + +* Rozdej karty +* Dokud hráč nevyhrál: + * Zobraz stav hry + * Zeptej se hráče, kam chce hrát + * Je-li to možné: + * Proveď tah + * Jinak: + * Vynadej hráči, že daný tah nedává smysl +* Pogratuluj hráči + +(Hráč může i prohrát, ale na to může přijít sám a hru ukončit.) + +Minule jsme počítač naučil{{gnd('i', 'y', both='i')}} co to je *karta* +a jak vytvořit zamíchaný *balíček*. +Pojďme se konečně vrhnout na první krok výše: rozdávání. + + +## Rozdání sloupečků + +Karty se určitým způsobem *rozdají* do několika balíčků, hromádek nebo +jiných skupin. +Pro přehlednost si tyto skupiny označíme písmenky: + +* Dobírací balíčky `U`, `V`, ze kterých se berou karty. +* Cílové hromádky `W`-`Z`, kam se dávají seřazené karty. Cíl hry je do těchto + hromádek dát všechny karty. +* 7 sloupečků `A`-`G`, kde hráč může s kartami manipulovat. + +Prvotní rozdání karet spočívá v tom, že rozdáš karty do 7 sloupečků. +Nerozdané karty zůstanou v balíčku `U`; ostatní místa na karty budou prázdná: + +{{ figure(img=static('game.png'), alt="Ukázka sloupečků") }} + +```plain + U V W X Y Z + [???] [ ] [ ] [ ] [ ] [ ] + + A B C D E F G + [3♣ ] [???] [???] [???] [???] [???] [???] + [5 ♥] [???] [???] [???] [???] [???] + [6♣ ] [???] [???] [???] [???] + [5♠ ] [???] [???] [???] + [Q ♥] [???] [???] + [4♠ ] [???] + [3 ♦] +``` + +V N-tém sloupečku (počítáno od nuly) je N +karet rubem nahoru plus jedna karta lícem nahoru. +Karty do sloupečků se z balíčku rozdávají postupně: vždy se lízne +vrchní (poslední) karta z balíčku a dá se na konec sloupečku. + + +Napiš následující funkci: + +```python +def rozdej_sloupecky(balicek): + """Rozdá z daného balíčku 7 "sloupečků" -- seznamů karet + + Karty ve sloupečcích jsou odstraněny z balíčku. + Vrátí všechny sloupečky -- tedy seznam (nebo n-tici) sedmi seznamů. + """ +``` + +Například: + +```pycon +>>> balicek = priprav_balicek() +>>> sloupecky = rozdej_sloupecky(balicek) +>>> popis_seznam_karet(sloupecky[0]) +[3♣ ] +>>> popis_seznam_karet(sloupecky[1]) +[???] [5 ♥] +>>> popis_seznam_karet(sloupecky[2]) +[???] [???] [6♣ ] +>>> popis_seznam_karet(sloupecky[6]) +[???] [???] [???] [???] [???] [???] [3 ♦] +>>> len(balicek) # Z balíčku zmizely karty, které jsou ve sloupečcích +24 +``` + +Jak tahle funkce funguje? + +* Vytvoří prázdný seznam sloupečků +* Sedmkrat (pro N od 0 do 6): + * Vytvoří prázdný sloupeček (seznam) + * N-krát za sebou: + * „Lízne“ (`pop`) kartu zvrchu balíčku + * Dá líznutou kartu na vršek sloupečku (`append`) + * „Lízne“ (`pop`) kartu zvrchu balíčku + * Líznutou kartu otočí lícem nahoru (`otoc_kartu`) + a dá vršek sloupečku (`append`) + * Hotový sloupeček přidá do seznamu sloupečků +* Výsledné sloupečky vrátí + +Pro ověření spusť testy: + +* level 40: Funkce existuje +* level 41: Funkce vrací seznam sedmi seznamů +* level 42: + * V každém sloupečku je aspoň jedna karta + * Poslední karta je lícem nahoru +* level 43: V každém sloupečku je správný počet karet rubem nahoru + + +## Vypsání sloupečků + +Vzpomínáš si na základní schéma hry? + +* Rozdej karty +* Dokud hráč nevyhrál: + * Zobraz stav hry + * Zeptej se hráče, kam chce hrát + * Je-li to možné: + * Proveď tah + * Jinak: + * Vynadej hráči, že daný tah nedává smysl +* Pogratuluj hráči + +Rozdání balíčku a sloupečků už víceméně máš! +Pro teď přeskočíme zjišťování, jestli hráč vyhrál, a koukneme se na zobrazení +stavu hry. + +Například, pokud jsou sloupečky tyto: + +```python +sloupecky = [ + [(1, 'Pi', True), (7, 'Sr', True)], + [(2, 'Sr', True), (6, 'Ka', True)], + [(3, 'Ka', True), (5, 'Kr', False)], + [(4, 'Kr', False), (4, 'Pi', True)], + [(5, 'Pi', False), (3, 'Sr', True)], + [(6, 'Sr', True), (2, 'Ka', True)], + [(7, 'Ka', True), (1, 'Kr', True), (10, 'Ka', True)], +] +``` + +… můžeš je vypsat jednotlivě: + + +```pycon +>>> for sloupecek in sloupecky: +>>> print(popis_seznam_karet(sloupecek)) +[A♠ ] [7 ♥] +[2 ♥] [6 ♦] +[3 ♦] [???] +[???] [4♠ ] +[???] [3 ♥] +[6 ♥] [2 ♦] +[7 ♦] [A♣ ] [X ♦] +``` + +To ale není to, co chceme vypsat ve hře: tam se karty v jednom sloupečku +ukazují pod sebou. + +Budeš potřebovat na prvním řádku ukázat první karty ze všech sloupečků, +na druhém řádku druhé karty ze všech sloupečků, na třetím třetí, atd. +Pro příklad výše by tedy mělo vyjít: + + +```plain +[A♠ ] [2 ♥] [3 ♦] [???] [???] [6 ♥] [7 ♦] +[7 ♥] [6 ♦] [???] [4♠ ] [3 ♥] [2 ♦] [A♣ ] + [X ♦] +``` + +Znáš funkci, která vezme několik seznamů, a dá ti k dispozici napřed první +prvky těch seznamů, potom druhé, a tak dál? +Zkus ji použít! + +Pozor, bude tu potřeba pořádně se zamyslet. + +```python +def vypis_sloupecky(sloupecky): + """Vypíše sloupečky textově. + + Tato funkce je jen pro zobrazení, používá proto přímo funkci print() + a nic nevrací. + """ +``` + +* level 50: Funkce existuje +* level 51: Funkce vypisuje karty ze věch sloupečků +* level 52: Funkce funguje, když jsou sloupečky nestejně dlouhé. (Na prázdné místo patří 5 mezer.) + + +## Práce se sloupečky + +Aby sis v budoucnu ušetřil{{a}} práci, a aby sis procvičila seznamy, +zkus teď napsat dvě funkce, které přesunují karty mezi balíčky. + +Použij na to metody seznamů (`append`, `extend`, `pop`, příkaz `del`) +a pomocné funkce, které už máš (`otoc_kartu`). + +```python +def presun_kartu(sloupec_odkud, sloupec_kam, pozadovane_otoceni): + """Přesune vrchní kartu ze sloupce "odkud" do sloupce "kam". + Karta bude otocena lícem nebo rubem nahoru podle "pozadovane_otoceni". + """ + +def presun_nekolik_karet(sloupec_odkud, sloupec_kam, pocet): + """Přesune "pocet" vrchních karet ze sloupce "odkud" do sloupce "kam". + Karty se přitom neotáčí. + """ +``` + +* level 60: Funkce `presun_kartu` existuje +* level 61: Funkce `presun_kartu` funguje dle zadání +* level 70: Funkce `presun_nekolik_karet` existuje +* level 71: Funkce `presun_nekolik_karet` funguje dle zadání diff --git a/lessons/klondike/decks/info.yml b/lessons/klondike/decks/info.yml new file mode 100644 index 00000000..58f5b2c3 --- /dev/null +++ b/lessons/klondike/decks/info.yml @@ -0,0 +1,4 @@ +title: "Klondike: Balíčky" +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. +license: cc-by-sa-40 diff --git a/lessons/klondike/decks/static/game.png b/lessons/klondike/decks/static/game.png new file mode 100644 index 00000000..1d171a3c Binary files /dev/null and b/lessons/klondike/decks/static/game.png differ diff --git a/lessons/klondike/decks/static/klondike.png b/lessons/klondike/decks/static/klondike.png new file mode 100644 index 00000000..61d321e4 Binary files /dev/null and b/lessons/klondike/decks/static/klondike.png differ diff --git a/lessons/klondike/game/index.md b/lessons/klondike/game/index.md new file mode 100644 index 00000000..148f96ad --- /dev/null +++ b/lessons/klondike/game/index.md @@ -0,0 +1,279 @@ +# Klondike Solitaire: Hra + +Klondike Solitaire zbývá dát konečně dohromady kousky, které jsme +v několika posledních lekcích připravovali! + +V těchto materiálech najdeš hotové funkce, které je dobré si prohlédnout +a porozumět jim, ale pak si je můžeš zkopírovat do svého kódu. +Velké procvičení seznamů a slovníků přijde na konci. + +## Hra + +Vzpomínáš si na schéma hry? + +* Rozdej balíček a sloupečky karet +* Dokud hráč nevyhrál: + * Zobraz stav hry + * Zeptej se hráče, odkud a kam chce hrát + * Je-li to možné: + * Proveď tah + * Jinak: + * Vynadej hráči, že daný tah nedává smysl +* Pogratuluj hráči + +V Pythonu to bude vypadat následovně. +Program si ulož do modulu `hra.py`: + +```python +from klondike import udelej_hru, vypis_hru, presun_kartu, presun_nekolik_karet + +print() + +hra = udelej_hru() + +while not hrac_vyhral(hra): + vypis_hru(hra) + odkud, kam = nacti_tah() + try: + udelej_tah(hra, odkud, kam) + except ValueError as e: + print('Něco je špatně:', e) + +vypis_hru(hra) +print('Gratuluji!') +``` + +K tomu, abys doplnila funkce do této hry, budeš potřebovat namodelovat +onu `hru`. +Ta se skládá z několika balíčků/sloupečků, tedy seznamů karet. +Ve výpisu butou pojmenované A-Z: + +{{ figure(img=static('game.png'), alt="Ukázka sloupečků") }} + +* `U` je dobírací balíček, ze kterého se doplňuje `V`. +* `V` je balíček, ze kterého můžeš brát karty +* `W-Z` jsou cílové hromádky. Cílem hry je na ně přemístit všechny + karty. +* `A-G` jsou sloupečky, kde se karty dají přeskládávat. + +Těchto 13 pojmenovaných seznamů reprezentuje celý stav rozehrané hry. +Hru proto budeme reprezentovat slovníkem, kde klíče budou písmenka +a hodnoty pak jednotlivé seznamy. + +Následující funkce takovou hru vytvoří: + +```python +def udelej_hru(): + """Vrátí slovník reprezentující novou hru. + """ + balicek = vytvor_balicek() + + hra = { + 'U': balicek, + } + + # V-Z začínají jako prázdné seznamy + for pismenko in 'VWXYZ': + hra[pismenko] = [] + + # A-G jsou sloupečky + for pismenko, sloupec in zip('ABCDEFG', rozdej_sloupecky(balicek)): + hra[pismenko] = sloupec + + return hra +``` + +Další funkce, `vypis_hru`, hru vypíše do konzole pomocí `print`. +Výsledek bude něco jako: + +```plain + U V W X Y Z + [???] [ ] [ ] [ ] [ ] [ ] + + A B C D E F G + [3♣ ] [???] [???] [???] [???] [???] [???] + [5 ♥] [???] [???] [???] [???] [???] + [6♣ ] [???] [???] [???] [???] + [5♠ ] [???] [???] [???] + [Q ♥] [???] [???] + [4♠ ] [???] + [3 ♦] +``` + +V téhle funkci není nic moc objevného a testům záleží na každé mezeře, +takže si ji určitě zkopíruj: + +```python +def vypis_hru(hra): + """Vypíše hru textově. + + Tato funkce je jen pro zobrazení, používá proto přímo funkci print() + a nic nevrací. + """ + print() + print(' U V W X Y Z') + print('{} {} {} {} {} {}'.format( + popis_vrchni_kartu(hra['U']), + popis_vrchni_kartu(hra['V']), + popis_vrchni_kartu(hra['W']), + popis_vrchni_kartu(hra['X']), + popis_vrchni_kartu(hra['Y']), + popis_vrchni_kartu(hra['Z']), + )) + print() + print(' A B C D E F G') + vypis_sloupecky([ + hra['A'], hra['B'], hra['C'], hra['D'], hra['E'], hra['F'], hra['G'] + ]) + print() +``` + +Pro kontrolu můžeš pustit testy: + +* Level 70: Funkce `udelej_hru` existuje +* Level 71: Funkce `udelej_hru` funguje dle zadání +* Level 80: Funkce `vypis_hru` existuje +* Level 81: Funkce `vypis_hru` funguje dle zadání + + +## Načtení tahu + +Hra se bude ovládat zadáním dvou jmen balíčku: odkud a kam hráč chce kartu +přesunout. + +Tahle funkce není součást logiky hry. Dej ji do `hra.py`, hned za `import`. + +```python +def nacti_tah(): + while True: + tah = input('Tah? ') + try: + jmeno_zdroje, jmeno_cile = tah.upper() + except ValueError: + print('Tah zadávej jako dvě písmenka, např. UV') + else: + return jmeno_zdroje, jmeno_cile +``` + +## Zástupné funkce + +K úplné hře nám chybí ještě samotná logika hry: `hrac_vyhral` a `udelej_tah`. + +Aby ti hra aspoň trochu fungovala, vytvoř si zástupné funkce, +které nic nekontrolují a nenechají tě vyhrát. +Dej ji do `hra.py`, opět hned za `import`: + +```python +def hrac_vyhral(hra): + """Vrací True, pokud je hra vyhraná. + """ + return False + +def udelej_tah(hra, jmeno_odkud, jmeno_kam): + """Udělá tah z jednoho místa na druhé. + + Místa jsou označovány velkými písmeny (např. 'A', 'V' nebo 'X'). + + Není-li tah možný, vyhodí ValueError s popisem problému. + """ + presun_kartu(hra[jmeno_odkud], hra[jmeno_kam], True) +``` + +Obě bude ještě potřeba upravit, ale teď už si můžeš hru víceméně zahrát! +Zkus si to! + + +## Jiné rozhraní + +Celý tento projekt píšeš ve funkcích s daným jménem a s daným počtem a významem +argumentů. +To má dvě výhody. + +První z nich je testování: připravené testy importují tvé funkce a zkouší je, +takže si můžeš být jist{{a}}, že fungují. + +Druhá je zajímavější: máš-li logiku hry, funkce `udelej_hru` `udelej_tah` +a `hrac_vyhral`, napsané podle specifikací, může je použít i jakýkoli jiný +program – ne jen ten, který jsi napsal{{a}} ty. + +Jeden takový si můžeš vyzkoušet: + +* Nainstaluj si do virtuálního prostředí knihovnu `pyglet`, která umí ovládat + grafická okýnka: + + ```console + (venv)$ python -m pip install pyglet + ``` + +* Stáhni si do aktuálního adresáře soubory [ui.py] a [cards.png]. + + [ui.py]: {{ static('ui.py') }} + [cards.png]: {{ static('cards.png') }} + +* Hru spusť pomocí: + + ```console + (venv)$ python ui.py + ``` + +Hra považuje chyby `ValueError` za chyby uživatele, tedy tahy proti pravidlům. +Zobrazí je v terminálu a v titulku okýnka. +Ostatní chyby by ve správném programu neměly nastat; objeví se jako normální +chybové hlášky na terminálu. + +*Obrázky karet jsou z [Board Game Pack](https://kenney.nl/assets/boardgame-pack) +studia [kenney.nl](https://kenney.nl).* + + +## Logika hry + +Zbývá doplnit „pravidla hry“ do dvou funkcí, `hrac_vyhral` a `udelej_tah`. +To už bude na tobě. + +### hrac_vyhral + +Hráč vyhrál, pokud jsou všechny karty na cílových hromádkách `W`-`Z`. + +### udelej_tah + +Když tah není podle pravidel, funkce `udelej_tah` vyhodí `ValueError`. + +Možné tahy: +* `U`→`V`: + * V balíčku `U` musí něco být + * Přesouvá se jedna karta; otočí se lícem nahoru +* `V`→`U`: + * V balíčku U nesmí být nic + * Přesouvají se všechny karty, seřazené v opačném pořadí; + otočí se rubem nahoru (tj. volej dokola + `presun_kartu(hra['V'], hra['U'], False)` dokud ve V něco je) +* Balíček `V` nebo sloupeček `A`-`G` (zdroj) → hromádka `W`-`Z`: + * Přesouvá se jedna karta + * Je-li cílová hromádka prázdná: + * Musí to být eso + * Jinak: + * Přesouvaná karta musí mít stejnou barvu jako vrchní karta cílové hromádky + * Přesouvaná karta musí být o 1 vyšší než vrchní karta cílové hromádky + * Je-li zdroj po přesunu neprázdný, jeho vrchní karta se otočí lícem nahoru +* Balíček `V` → „cílový“ sloupeček `A`-`G` + * Přesouvá se jedna karta + * Přesouvaná karta musí pasovat\*⁾ na cílový sloupeček +* „Zdrojový“ sloupeček `A`-`G` → „cílový“ sloupeček `A`-`G` + * Přesouvá se několik karet + * (zkontroluj všechny možnosti: 1 až počet karet ve zdrojovém sloupečku; + vždy je max. jedna správná možnost) + * Všechny přesouvané karty musí být otočené lícem nahoru + * První z přesouvaných karet musí pasovat\*⁾ na cílový sloupeček +* Cíl `W`-`Z` → sloupeček `A`-`G` (nepovinné – jen v některých variantách hry) + * Přesouvá se jedna karta + * Přesouvaná karta musí pasovat\*⁾ na cílový sloupeček + +\*⁾ Kdy přesouvaná karta pasuje na sloupeček? +* Je-li sloupeček prázdný: + * Karta musí být král +* Jinak: + * Barva přesouvané karty musí být opačná než barva vrchní karty sloupečku, tedy: + * Červená (♥ nebo ♦) jde dát jen na černou (♠ nebo ♣) + * Černá (♠ nebo ♣) jde dát jen na červenou (♥ nebo ♦) + * A zároveň musí hodnota přesouvané karty být o 1 nižší než hodnota vrchní + karty sloupečku. diff --git a/lessons/klondike/game/info.yml b/lessons/klondike/game/info.yml new file mode 100644 index 00000000..15599682 --- /dev/null +++ b/lessons/klondike/game/info.yml @@ -0,0 +1,4 @@ +title: "Klondike: Hra" +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. +license: cc-by-sa-40 diff --git a/lessons/klondike/game/static/cards.png b/lessons/klondike/game/static/cards.png new file mode 100644 index 00000000..832778da Binary files /dev/null and b/lessons/klondike/game/static/cards.png differ diff --git a/lessons/klondike/game/static/game.png b/lessons/klondike/game/static/game.png new file mode 100644 index 00000000..1d171a3c Binary files /dev/null and b/lessons/klondike/game/static/game.png differ diff --git a/lessons/klondike/game/static/klondike.png b/lessons/klondike/game/static/klondike.png new file mode 100644 index 00000000..61d321e4 Binary files /dev/null and b/lessons/klondike/game/static/klondike.png differ diff --git a/lessons/klondike/game/static/ui.py b/lessons/klondike/game/static/ui.py new file mode 100644 index 00000000..7be6ce53 --- /dev/null +++ b/lessons/klondike/game/static/ui.py @@ -0,0 +1,176 @@ +from pathlib import Path +import traceback + +import pyglet + +from klondike import udelej_hru, udelej_tah + + +WINDOW_CAPTION = 'Klondike Solitaire' + +SUIT_NAMES = 'Kr', 'Ka', 'Pi', 'Sr' + +KEYS = { + pyglet.window.key.A: 'A', + pyglet.window.key.B: 'B', + pyglet.window.key.C: 'C', + pyglet.window.key.D: 'D', + pyglet.window.key.E: 'E', + pyglet.window.key.F: 'F', + pyglet.window.key.G: 'G', + pyglet.window.key.U: 'U', + pyglet.window.key.V: 'V', + pyglet.window.key.W: 'W', + pyglet.window.key.X: 'X', + pyglet.window.key.Y: 'Y', + pyglet.window.key.Z: 'Z', +} + + +image = pyglet.image.load('cards.png') +card_width = image.width // 14 +card_height = image.height // 4 + +card_pictures = {} +for suit_number, suit_name in enumerate(SUIT_NAMES): + for value in range(1, 14): + card_pictures[value, suit_name] = image.get_region( + card_width * value, card_height * suit_number, + card_width, card_height, + ) + +card_back_picture = image.get_region(0, card_height, card_width, card_height) +empty_slot_picture = image.get_region(0, 0, card_width, card_height) + +label = pyglet.text.Label('x', color=(0, 200, 100, 255), + anchor_x='center', anchor_y='center') + +window = pyglet.window.Window(resizable=True, caption=WINDOW_CAPTION) +press_queue = [] + + +def get_dimensions(): + card_width = window.width / (7*6+1) * 5 + card_height = card_width * 19/14 + margin_x = card_width / 5 + margin_y = margin_x * 2 + offset_y = margin_x + return card_width, card_height, margin_x, margin_y, offset_y + + +def draw_card(card, x, y, x_offset=0, y_offset=0, active=False): + card_width, card_height, margin_x, margin_y, offset_y = get_dimensions() + + if card == None: + pyglet.gl.glColor4f(0.5, 0.5, 0.5, 0.5) + picture = empty_slot_picture + else: + if active: + pyglet.gl.glColor4f(0.75, 0.75, 1, 1) + else: + pyglet.gl.glColor4f(1, 1, 1, 1) + value, suit, is_face_up = card + if is_face_up: + picture = card_pictures[value, suit] + else: + picture = card_back_picture + + picture.blit( + margin_x + (card_width + margin_x) * x + (x_offset * margin_x / 60), + window.height - (margin_y + card_height) * (y+1) - offset_y * y_offset, + width=card_width, height=card_height) + + +def draw_label(text, x, y, active): + card_width, card_height, margin_x, margin_y, offset_y = get_dimensions() + + label.x = x * (card_width + margin_x) + margin_x + card_width / 2 + label.y = window.height - y * (card_height + margin_y) - margin_y / 2 + label.text = text + if active: + label.color = 200, 200, 255, 255 + else: + label.color = 0, 200, 100, 255 + label.draw() + + +def draw_deck(letter, deck, x, y, x_offset=0, y_offset=0): + active = (letter in press_queue) + draw_label(letter, x, y, active) + draw_card(None, x, y) + for i, card in enumerate(deck): + draw_card(card, x, y, x_offset*i, y_offset*i, active) + + +@window.event +def on_draw(): + # Enable transparency for images + pyglet.gl.glEnable(pyglet.gl.GL_BLEND) + pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA) + + # Green background + pyglet.gl.glClearColor(0, 0.25, 0.05, 1) + window.clear() + + # Get dimensions + card_width, card_height, margin_x, margin_y, offset_y = get_dimensions() + label.font_size = margin_y / 2 + + # Draw all the cards in the various decks + for x, letter in enumerate('UV'): + draw_deck(letter, game[letter], x, 0, x_offset=1) + + for x, letter in enumerate('WXYZ'): + draw_deck(letter, game[letter], x + 3, 0, x_offset=1) + + for x, letter in enumerate('ABCDEFG'): + draw_deck(letter, game[letter], x, 1, y_offset=1) + + +@window.event +def on_key_press(symbol, mod): + if symbol in KEYS: + press_queue.append(KEYS[symbol]) + handle_press_queue() + + +@window.event +def on_mouse_press(x, y, symbol, mod): + card_width, card_height, margin_x, margin_y, offset_y = get_dimensions() + if y > window.height - card_height - margin_y: + deck_names = 'UV WXYZ' + else: + deck_names = 'ABCDEFG' + deck_name = deck_names[int(x - margin_x/2) // int(card_width + margin_x)] + if deck_name.strip(): + press_queue.append(deck_name) + handle_press_queue() + + +def handle_press_queue(): + if press_queue == ['U']: + press_queue.append('V') + if len(press_queue) >= 2: + source = press_queue[0] + destination = press_queue[1] + press_queue.clear() + + try: + result = udelej_tah(game, source, destination) + except ValueError as e: + # Print *just* the error message + msg = f'{source}→{destination}: {e}' + window.set_caption(msg) + print(msg) + except Exception: + # Print the error message, but ignore the error (so the + # game can continue) + traceback.print_exc() + else: + print(f'{source}→{destination}: {result}') + window.set_caption(WINDOW_CAPTION) + + +game = udelej_hru() + +pyglet.app.run()