diff --git a/tp8/bataille-carte/apl1test.py b/tp8/bataille-carte/apl1test.py new file mode 100644 index 0000000000000000000000000000000000000000..8533ccaca5d99e7cfb83d6d86aa9334bb6a73a40 --- /dev/null +++ b/tp8/bataille-carte/apl1test.py @@ -0,0 +1,89 @@ +import thonnycontrib +from thonnycontrib.backend.evaluator import Evaluator +import thonnycontrib.backend.l1test_backend +from thonny.plugins.cpython_backend.cp_back import MainCPythonBackend +import thonnycontrib.backend.doctest_parser +from thonnycontrib.backend.doctest_parser import ExampleWithExpected, ExampleWithoutExpected +import thonnycontrib.backend.ast_parser +from thonnycontrib.backend.ast_parser import L1DocTest +import thonnycontrib.backend.verdicts +from thonnycontrib.backend.verdicts.ExceptionVerdict import ExceptionVerdict + +import inspect +import tempfile +import os +import sys + +class MockBackend(MainCPythonBackend): + """ + Fake backend. + """ + def __init__(self): + ... + + def send_message(self, msg) -> None: + ... + +# register backend +thonnycontrib.backend.l1test_backend.BACKEND = MockBackend() + +def l1test_to_org(filename: str, source: str=""): + """ + Return an org abstract of the tests presents in `filename` file. + """ + abstract = {'total': 0, + 'success': 0, + 'failures': 0, + 'errors': 0, + 'empty': 0} + + if source == "": + with open(filename, 'rt') as fin: + source = fin.read() + evaluator = Evaluator(filename=filename, + source=source) + tests = evaluator.evaluate() + n = len(tests) + abstract['total'] = n + res = "" + for test in tests: + examples = test.get_examples() + res_examples = "" + nb_test, nb_test_ok = 0, 0 + empty = True + for example in examples: + verdict = test.get_verdict_from_example(example) + if isinstance(example, ExampleWithExpected): + nb_test += 1 + if verdict.isSuccess(): + nb_test_ok += 1 + abstract['success'] += 1 + else: + abstract['failures'] += 1 + empty = False + if isinstance(verdict, ExceptionVerdict): + abstract['errors'] += 1 + empty = False + res_examples += f"** {verdict}\n\n" + if not verdict.isSuccess(): + res_examples += f" {verdict.get_details()}\n\n" + if not empty: + res += f"* {test.get_name()} ~ {nb_test_ok}/{nb_test} réussis\n\n" + else: + abstract['empty'] += 1 + res += f"* {test.get_name()}\n\n Aucun test trouvé !\n\n" + res += res_examples + res = f"Tests exécutés : {abstract['total']}\nSuccès: {abstract['success']}, \ +Echecs: {abstract['failures']}, Erreurs: {abstract['errors']}, \ +Vide: {abstract['empty']}\n\n" + res + return res + + +def testmod(modulename: str): + """ + mimic the doctest.testmod function + for `modulename` module + """ + print(l1test_to_org(modulename)) + + diff --git a/tp8/bataille-carte/apqueue.py b/tp8/bataille-carte/apqueue.py new file mode 100644 index 0000000000000000000000000000000000000000..cdc44f7d34f0c344ae262e89fb6af1a93e633d08 --- /dev/null +++ b/tp8/bataille-carte/apqueue.py @@ -0,0 +1,104 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`apqueue` module + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2015, september +:last revision: 2024, March + +A module for queue data structure. + +:Provides: + +* class ApQueue + +and methods + +* `enqueue` +* `dequeue` +* `is_empty` +""" +from typing import TypeVar +T = TypeVar('T') + +class ApQueueEmptyError(Exception): + """ + Exception for empty stacks + """ + def __init__(self, msg): + self.message = msg + + +class ApQueue(): + """ + $$$ ap_queue = ApQueue() + $$$ ap_queue.is_empty() + True + $$$ ap_queue.enqueue(1) + $$$ ap_queue.is_empty() + False + $$$ ap_queue.enqueue(2) + $$$ str(ap_queue) + '→2|1→' + $$$ ap_queue.dequeue() + 1 + $$$ ap_queue.dequeue() + 2 + $$$ ap_queue.is_empty() + True + $$e ap_queue.dequeue() + ApQueueEmptyError + """ + ARROW = chr(0x2192) + + def __init__(self): + """ + build a new empty queue + precondition: none + """ + self.__content = [] + + def enqueue(self, elt: T): + """ + insert an element at the begining of the queue + precondition: none + """ + self.__content.insert(0, elt) + + def dequeue(self) -> T: + """ + return the element on top of self + Side effect: self contains an element less + precondition: self must be non empty + """ + if len(self.__content) > 0: + res = self.__content.pop() + else: + raise ApQueueEmptyError('empty queue, nothing to dequeue') + return res + + def is_empty(self) -> bool: + """ + return: + * ``True`` if s is empty + * ``False`` otherwise + precondition: none + """ + return self.__content == [] + + def __str__(self) -> str: + """ + return the string representation of this queue. + """ + return ApQueue.ARROW + \ + "|".join(str(el) for el in self.__content) + \ + ApQueue.ARROW + + +if __name__ == '__main__': + import apl1test + apl1test.testmod('apqueue.py') diff --git a/tp8/bataille-carte/apstack.py b/tp8/bataille-carte/apstack.py new file mode 100644 index 0000000000000000000000000000000000000000..d9a512110ce56dc25f67de8dd915988c88af22de --- /dev/null +++ b/tp8/bataille-carte/apstack.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`stack` module + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2015, september +:last revision: 2017, october + +A module for stack data structure. + +:Provides: + +* class ApStack + +and methods + +* `push` +* `pop` +* `top` +* `is_empty` + +:Examples: +""" +from typing import TypeVar +T = TypeVar('T') + +class ApStackEmptyError(Exception): + """ + Exception for empty stacks + """ + def __init__(self, msg): + self.message = msg + + +class ApStack(): + """ + $$$ stak = ApStack() + $$$ stak.is_empty() + True + $$$ stak.push(1) + $$$ stak.is_empty() + False + $$$ stak.push(2) + $$$ stak.top() + 2 + $$$ stak.pop() + 2 + $$$ stak.top() + 1 + $$$ stak.pop() + 1 + $$$ stak.is_empty() + True + $$e stak.pop() + ApStackEmptyError + """ + + def __init__(self): + """ + build a new empty stack + précondition : none + """ + self.__content = [] + + def push(self, el: T): + """ + add el on top of the stack. + précondition : none + """ + self.__content.append(el) + + def pop(self) -> T: + """ + return the element on top of self + + Side effect: self contains an element less + + précondition : self must be non empty + """ + if len(self.__content) == 0: + raise ApStackEmptyError('empty stack, nothing to pop') + return self.__content.pop() + + def top(self) -> T: + """ + return the element on top of self without removing it + + précondition : self must be non empty + """ + if len(self.__content) == 0: + raise ApStackEmptyError('empty stack, nothing to pop') + return self.__content[-1] + + def is_empty(self) -> bool: + """ + return: + * ``True`` if s is empty + * ``False`` otherwise + précondition : none + """ + return self.__content == [] + + def __str__(self) -> str: + """ + return a stack representation + """ + mlen = 1 + if not self.is_empty(): + mlen = max(len(str(el)) for el in self.__content) + res = [] + for el in self.__content: + pad = mlen - len(str(el)) + left = pad // 2 + right = pad - left + res.insert(0, "|" + " " * left + str(el) + " " * right + "|") + res.append("+" + "-" * mlen + "+") + return "\n".join(res) + + +if (__name__ == '__main__'): + import apl1test + apl1test.testmod('apstack.py') diff --git a/tp8/bataille-carte/card.py b/tp8/bataille-carte/card.py new file mode 100755 index 0000000000000000000000000000000000000000..04c1532f95d2f3f75c6270c5fc844ccf7156faeb --- /dev/null +++ b/tp8/bataille-carte/card.py @@ -0,0 +1,168 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`card` module + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2017, september. +:last revision: 2024, march + +""" +from __future__ import annotations +import random + + + +class Card(object): + """ + Cards are defined by a value and a color. + Possible values and colors are listed in ``Card.VALUES`` and ``Card.COLORS``. + + $$$ c1 = Card("Ace", "heart") + $$$ c1.color + 'heart' + $$$ c1.value + 'Ace' + $$$ repr(c1) + 'Card("Ace", "heart")' + $$$ c2 = Card("King", "spade") + $$$ c2.value in Card.VALUES + True + $$$ c2.color in Card.COLORS + True + $$$ c1 == c1 + True + $$$ c1 != c1 + False + $$$ c1 < c1 + False + $$$ c1 <= c1 + True + """ + # tuple of possible values and colors in ascending order + VALUES = ("Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Knight", "Queen", "King") + COLORS = ("spade", "heart", "diamond", "club") + + def __init__(self, value: str, color: str): + """ + creates a card with value and color + + précondition : value in VALUES and color in COLORS + """ + if value in Card.VALUES: + self.value = value + if color in Card.COLORS: + self.color = color + + def __hash__(self) -> int: + """ + Renvoie un haché de self. + """ + return hash((self.color,self.value)) + + def __repr__(self) -> str: + """ + return a string representation of the card + + $$$ repr(Card('Ace', 'heart')) + 'Card("Ace", "heart")' + """ + return f"(Card("f"{self.value}, "f"{self.color})" + + def compare(self, card: Card) -> int: + """ + compares cards. + + Order on cards is defined by order on values + + return: + + * a positive number if self is greater than card + * a negative number if self is lower than card + * 0 if self is the same than card + + précondition: none + exemples: + + $$$ c1 = Card('Ace', 'heart') + $$$ c2 = Card('King', 'heart') + $$$ c3 = Card('Ace','spade') + $$$ c1bis = Card('Ace','heart') + $$$ c1.compare(c2) < 0 + True + $$$ c2.compare(c1) > 0 + True + $$$ c1.compare(c3) == 0 + True + """ + ... + + @staticmethod + def deck(n_card: int) -> list[Card]: + """ + return a list of `n_card` randomly chosen cards + + precondition: n_card > 0 and n_card <= 4*13 + + Exemples: + + $$$ cartes = Card.deck( 10 ) + $$$ len(cartes) == 10 + True + $$$ all( isinstance(c, Card) for c in cartes) + True + $$$ len(set(cartes)) + len(cartes) + """ + ... + + def __eq__(self, card: Card) -> bool: + """ + return True if self equals card + False otherwise + """ + ... + + def __neq__(self, card: Card) -> bool: + """ + return True if self don't equal card + False otherwise + """ + ... + + def __lt__(self, card: Card) -> bool: + """ + return True if self is strictly inferior to card + False otherwise + """ + ... + + def __le__(self, card: Card) -> bool: + """ + return True if self is inferior or equal to card + False otherwise + """ + ... + + def __gt__(self, card: Card) -> bool: + """ + return True if self is strictly superior to card + False otherwise + """ + ... + + def __ge__(self, card: Card) -> bool: + """ + return True if self is superior or equal to card + False otherwise + """ + ... + + +if __name__ == '__main__': + import apl1test + apl1test.testmod('card.py') + diff --git a/tp8/bataille-carte/war.py b/tp8/bataille-carte/war.py new file mode 100644 index 0000000000000000000000000000000000000000..e1acaccd3e8c30fe38cf8862e58886e2ce5459e8 --- /dev/null +++ b/tp8/bataille-carte/war.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`war` game + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>` + +:date: 2021, april. +:last revision: 2024, march. +""" + +from card import Card +from apqueue import * +from apstack import * + + +def distribute(n_card: int) -> tuple[ApQueue, ApQueue]: + """ + renvoie un couple (m1, m2) constitué de deux files, + contenant pour chacune `n_card` cartes + + precondition : n_card > 0 + exemples : + + $$$ m1, m2 = distribute( 4 ) + $$$ len(m1) == 4 + True + $$$ len(m2) == 4 + True + $$$ type(m1) == ApQueue + True + $$$ type(m2) == ApQueue + True + $$$ carte = m1.dequeue() + $$$ isinstance(carte, Card) + True + """ + ... + +def gather_stack(main: ApQueue, pile: ApStack) -> None: + """ + ajoute les carte de la pile dans la main + + exemples : + + $$$ cartes = Card.deck(4) + $$$ main = ApQueue() + $$$ pile = ApStack() + $$$ for c in cartes: + ... pile.push(c) + $$$ gather_stack( main, pile ) + $$$ len( main ) == 4 + True + $$$ all( main.dequeue() == cartes[ 3 - i ] for i in range(3)) + True + """ + ... + +def play_one_round(m1: ApQueue, m2: ApQueue, pile: ApStack) -> None: + """ + Simule une étape du jeu : + `j1`` et ``j2`` prennent la première carte de leur + main. On compare les deux cartes : + + * Si la carte de ``j1`` est supérieure à celle de ``j2``, alors + ``j1`` remporte toutes les cartes de la pile ; + * Si la carte de ``j1`` est inférieure à celle de ``j2``, alors + ``j2`` remporte toutes les cartes de la pile ; + * Si les cartes sont égales, alors elles sont *empilées* sur la + pile. + + precondition : m1 et m2 ne sont pas vides + """ + ... + +def play(n_card: int, n_round: int) -> None: + """ + simule une partie de bataille + + n_card: le nombre de cartes à distribuer à chaque joueur. + n_round: le nombre maximal de tours + """ + ... + + +if __name__ == "__main__": + import apl1test + apl1test.testmod("war.py") +