pull/3/head
Marc CHEVALDONNE 11 months ago
parent bb137705e3
commit 4d026bef77

@ -1,2 +1,187 @@
# DouShouQi
# Introduction
- [Introduction](#introduction)
- [Dou Shou Qi](#dou-shou-qi)
- [**But du jeu** :](#but-du-jeu-)
- [**Matériel** :](#matériel-)
- [Pouvoir des animaux](#pouvoir-des-animaux)
- [Déroulement du jeu](#déroulement-du-jeu)
- [Déroulement des TP](#déroulement-des-tp)
- [Évaluation](#évaluation)
- [Structure de l'application](#structure-de-lapplication)
Cette introduction explique comment cette ressource va se dérouler, ce qu'il y aura à réaliser, et comment vous serez évaluées et évalués.
Pour découvrir le langage **Swift**, nous proposons de développer le modèle d'un jeu traditionnel chinois appelé le **Dou Shou Qi** dont les origines remontraient au Vème siècle.
# Dou Shou Qi
## **But du jeu** :
- occuper la tanière de son adversaire
- ou manger toutes ses pièces
- ou l'empêcher de bouger.
## **Matériel** :
Chaque joueur possède 8 pions numérotés (pour représenter leur force) et représentant des animaux : **Rat** 🐭 (1), **Chat** 🐱 (2), **Chien** 🐶 (3), **Loup** 🐺 (4), **Léopard** 🐆 (5), **Tigre** 🐯 (6), **Lion** 🦁 (7) et **Éléphant** 🐘 (8).
Le plateau de jeu est une grille à deux dimensions de 10 lignes et 7 colonnes. Il est fait de 4 types de cases :
- **Jungle** 🌿 : aucune particularité
- **Lac** 💧 : interdit à tous les animaux sauf le **Rat** ; le **Lion** et le **Tigre** peuvent sauter par-dessus.
- **Tanière** 🪹 : un joueur ne peut pas se déplacer sur sa propre **Tanière** et doit aller sur la **Tanière** de l'adversaire pour gagner
- **Piège** 🪤 : un pion sur un piège (quel que soit le côté du plateau) a une force de **0**.
La grille originale ressemble à :
🌿 🌿 🪤 🪹 🪤 🌿 🌿
🌿 🌿 🌿 🪤 🌿 🌿 🌿
🌿 🌿 🌿 🌿 🌿 🌿 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 🌿 🌿 🌿 🌿 🌿 🌿
🌿 🌿 🌿 🪤 🌿 🌿 🌿
🌿 🌿 🪤 🪹 🪤 🌿 🌿
(la tanière du haut est celle du joueur 1, celle du bas du joueur 2).
De plus, certaines cases sont les cases de départ des pions :
🦁 🌿 🪤 🪹 🪤 🌿 🐯
🌿 🐶 🌿 🪤 🌿 🐱 🌿
🐭 🌿 🐆 🌿 🐺 🌿 🐘
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🐘 🌿 🐺 🌿 🐆 🌿 🐭
🌿 🐱 🌿 🪤 🌿 🐶 🌿
🐯 🌿 🪤 🪹 🪤 🌿 🦁
(où les animaux du haut sont ceux du joueur 1, et ceux du bas du joueur 2).
## Pouvoir des animaux
- Tous les animaux peuvent _manger_ un animal de force égale ou inférieure, avec les exceptions suivantes :
- l'éléphant ne peut pas _manger_ le rat,
- le rat peut _manger_ l'éléphant,
- on ne peut pas _manger_ son propre animal.
- Les animaux ne peuvent se déplacer que sur une case adjacente (même ligne ou même colonne)
- Les animaux ne peuvent pas se déplacer sur leur propre **Tanière**
- Les animaux ne peuvent pas se déplacer dans l'eau (sauf le **Rat**). Attention, le **Rat** ne peut pas sortir de l'eau pour attaquer l'**éléphant** adverse, mais il peut le faire pour attaquer le **rat** adverse.
- Le **lion** et le **tigre** peuvent sauter par-dessus les lacs (en restant sur la même ligne ou sur la même colonne), **sauf** si un **rat** est dans le lac, sur la trajectoire du saut.
## Déroulement du jeu
Chaque joueur doit à son tour de jeu déplacer l'un de ses animaux.
# Déroulement des TP
L'objectif des TP est de réaliser cette application en 5 TP durant 6 semaines.
Les énoncés seront donnés en début de semaine ainsi qu'une liste de thème à travailler pour bien réussir le TP.
La sixième semaine sert de tampon, pour rattraper le retard accumulé pendant les semaines précédentes.
Pour chacune des 5 premières semaines, il est conseillé de :
- avant le TP :
- travailler les thèmes donnés en avance, via :
- la [documentation officielle du langage Swift](https://www.swift.org/documentation/)
- le [livre Apple sur le langage Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/)
- les [ressources de cours sur code first](https://codefirst.iut.uca.fr/documentation/mchCoursDocusaurus/docusaurus/Swift/)
- profiter du résumé en cours et poser des questions sur les parties non comprises
- pendant le TP :
- réaliser au maximum les tâches demandées
- et les faire valider par l'enseignant pour obtenir validation de l'acquisition des compétences
- après le TP :
- faire le point sur le "reste à faire" et s'organiser pour la semaine suivante
- faire le point sur les acquis que vous n'avez pas fait valider pour solliciter l'enseignant à la séance suivante
Pour la sixième semaine :
- profiter du dernier cours pour poser les dernières questions techniques
- profiter du dernier TP pour rattraper le retard et faire valider ses derniers acquis non validés par l'enseignant.
> Note :
> Les ressources de cours sur code first ne remplacent pas la documentation officielle. Elles apportent des conseils sur l'ordre de lecture dans la réalisation des TP.
>
# Évaluation
Une fiche de compétences à acquérir et des points à réaliser en TP sera préparée et accessible pour chaque TP sur code first.
Vous aurez accès aux évaluations de l'enseignant au fur et à mesure de son évaluation.
Pour obtenir une validation, vous devrez prouver votre acquisition des compétences à l'enseignant via : un entretien oral ou un examen écrit.
La note finale sera composée de l'ensemble des acquis validés par l'enseignant à la dernière minute du dernier TP.
# Structure de l'application
Voici un diagramme de classes volontairement simplifié présentant grossièrement les différents acteurs de l'application que vous devez réaliser :
```mermaid
classDiagram
direction TB
Game --> "1" Rules : rules
Game --> "1" Board : board
Game --> "2" Player: players
Rules <|.. ClassicRules
Rules <|.. VerySimpleRules
Rules ..> Board
Board --> "*,*" Cell : grid
Player <|-- RandomPlayer
Player <|-- HumanPlayer
class Board {
+insert(piece:, atRow:, andColumn:)
+removePiece(atRow:, andColumn:)
}
class Cell {
+cellType : CellType
+initialOwner: Owner
+piece : Piece?
}
class Rules {
+createBoard() : Board$
+getNextPlayer() : Owner
+isMoveValid(Board, Move) : Bool
+isGameOver(Board, Int, Int) : Bool
}
class Player {
+chooseMove(in: Board, with: Rules) Move?
}
class Game {
+init(withRules:, andPlayer1:, andPlayer2: Player)
+start()
}
```
- ```Board``` représente le plateau de jeu et est composé d'un tableau à deux dimensions de ```Cell``` : il est responsable de l'insertion, de la suppression et donc du déplacement des pions.
- ```Rules``` représente les règles du jeu. Comme elles sont assez touffues (```ClassicRules```), je vous proposerai des règles simplifiées (```VerySimpleRules```). ```Rules``` indique qui est le prochain joueur, si un coup est valide, si la partie est terminée, etc.
- ```Player``` représente un joueur. Nous en réaliserons deux versions en particulier : une *IA* qui jouera aléatoirement (```RandomPlayer```) et un joueur humain (```HumanPlayer```).
- ```Game``` : sert de médiateur et d'ordonnanceur pour la gestion globale du jeu.
L'application à réaliser est une application en lignes de commandes qui permettra de jouer dans un terminal et ressemblera au résultat ci-dessous :
<img src="./images/Terminal.png" width="600px"/>
Vous travaillerez sur :
- ```Board``` et ```Cell``` pendant les deux premières semaines,
- ```Rules``` et ```VerySimpleRules``` pendant la 3ème semaine,
- ```Player```, ```RandomPlayer``` et ```HumanPlayer``` pendant la 4ème semaine,
- ```Game``` et l'application finale pendant la 5ème semaine.
Des énoncés plus détaillés vous seront donnés au début de chaque semaine.
---
Copyright &copy; 2023-2024 Marc Chevaldonné
While writing this tutorial, I was listening to...
<table>
<tr>
<td>
<img src="./images/listeningto/on_the_corner.jpg" width="120"/>
</td>
<td>
<div>
<p><b>On The Corner</b></p>
<p><i>Miles Davis</i> (1972)</p>
</div>
</td>
</tr>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

@ -0,0 +1,321 @@
# Énoncé - Semaine 1
- [Énoncé - Semaine 1](#énoncé---semaine-1)
- [Délai de livraison](#délai-de-livraison)
- [Travail demandé](#travail-demandé)
- [Package 1 : Modèle](#package-1--modèle)
- [Package 2 : Extensions pour les tests en CLI](#package-2--extensions-pour-les-tests-en-cli)
- [Premier test : Command Line Tool](#premier-test--command-line-tool)
- [Critères d'évaluation](#critères-dévaluation)
# Délai de livraison
Votre travail est à rendre sur une branche tp1 et doit être fusionné à la branche principale lorsqu'il est terminé.
La livraison est attendue à la fin du TP.
Un retard jusqu'à la fin du second ou du troisième TP est autorisé mais se verra attribué des pénalités (cf. [Critères d'évaluation](#critères-dévaluation)).
# Travail demandé
Durant la première semaine, il vous est demandé de réaliser la première partie de la gestion du plateau de jeu.
## Package 1 : Modèle
Le premier *Package* à créer correspond au modèle (*Model*).
Voici un diagramme de classes de ce qui est attendu :
```mermaid
classDiagram
direction LR
class Board {
<<struct>>
+nbRows : Int
+nbColumns : Int
+init?(withGrid:)
}
class Cell {
<<struct>>
+init(ofType:ownedBy:withPiece:)
}
class CellType {
<<enum>>
unknown
jungle
water
trap
den
}
class Owner {
noOne
player1
player2
}
Cell --> "1" CellType : cellType
Cell --> "1" Owner : initialOwner
Board --> "*" Cell : grid
class Animal {
<<enum>>
rat
cat
dog
wolf
leopard
tiger
lion
elephant
}
class Piece {
<<struct>>
+init(withOwner:andAnimal:)
}
Piece --> "1" Owner : owner
Piece --> "1" Animal : animal
Cell --> "?" Piece : piece
```
En français, vous avez donc à préparer les types suivants :
- les *enums* :
- ```CellType``` : représente le type d'une cellule (case) du plateau de jeu. Ces cellules peuvent être de type jungle (```jungle```), lac (```water```), piège (```trap```) ou tannière (```den```). La valeur ```unknown``` sera surtout utile comme valeur par défaut et pour le débogage.
- ```Owner``` : représente le propriétaire d'une cellule du plateau (au démarrage du jeu, ou pour les tannières) ou d'une pièce. C'est un jeu à deux joueurs, les deux valeurs possibles sont donc ```player1``` et ```player2```. Mais puisqu'une cellule peut n'appartenir à personne, la valeur ```noOne``` existe également et est utilisée pour cela.
- ```Animal``` : représente le type d'animal du jeu (rat ```rat```, chat ```cat```, chien ```dog```, loup ```wolf```, lépoard ```leopard```, tigre ```tiger```, lion ```lion``` ou éléphant ```elephant```).
- ```Owner``` doit se transformer facilement en ```String``` pour permettre le débogage et le test en lignes de commande. Les transformations seront les suivantes :
valeur | String
--- | ---
```noOne``` | ```"x"```
```player1``` | ```"1"```
```player2``` | ```"2"```
- la structure ```Piece``` qui représente une pièce du jeu. Elle possède donc :
- deux propriétés en lecture seule, et non-modifiables :
- un propriétaire (```owner```)
- un type d'animal (```animal```)
- un initialiseur permettant d'écrire ces deux propriétés lors de l'initialisation
- la possibilité de transformer une valeur de ```Piece``` en ```String``` sous la forme : ```"[1:leopard]"``` par exemple pour un léopard du joueur 1.
- la structure ```Cell``` qui représente une cellule ou une case du jeu. Elle possède donc :
- des propriétés en lecture seule et non-modifiables :
- un type de case (```cellType```)
- un propriétaire de case (pour les tannières et les cases de départ) (```initialOwner```)
- éventuellement une pièce s'il y en a une dessus (```piece```)
- un initialiseur permettant de choisir le type de cellule, un propriétaire (avec la valeur par défaut ```noOne```), une pièce (par défaut il n'y en a pas)
- la possibilité de transformer une ```Cellule``` en ```String``` sous la forme : ```"<piece> on <cellType>, <owner>"``` (avec ```<piece>``` qui vaut ```ø``` s'il n'y a pas de pièce), par exemple : ```"[1:leopard] on .jungle, x"``` ou ```"ø on .water, x"``` ou ```"[2:tiger] on den, 1"```.
- la structure ```Board``` qui représente le plateau de jeu. Elle possède donc :
- des propriétés en lecture seule et non-modifiables :
- ```nbRows``` représentant le nombre de lignes du plateau de jeu,
- ```nbColumns``` représentant le nombre de colonnes du plateau de jeu.
- une propriété représentant un tableau à deux dimensions de cellules (```grid```). Cette propriété devra être uniquement lisible à l'extérieur de ```Board```, mais modifiable au sein de cette structure.
- un initialiseur prenant un tableau à deux dimensions de cellules en paramètre. Cet initialiseur devra faire des vérifications :
- toutes les lignes doivent avoir la même taille, sinon, l'initialiseur rend ```nil```.
## Package 2 : Extensions pour les tests en CLI
Pour permettre un affichage simplifié en lignes de commande, il vous est demandé de permettre l'affichage des pièces, cellules, etc. avec des emojis. Or, ceci étant spécifique à une application en lignes de commande, il n'est pas utile de les ajouter au *Package* précédent. En conséquence, il vous est demandé de créer un deuxième *Package* avec des extensions des types précédents afin de permettre l'affichage de :
- ```CellType``` (via une propriété ```symbol```) :
valeur | ```String```
--- | ---
```unknown``` | ```" "```
```jungle``` | ```"🌿"```
```den``` | ```"🪹"```
```trap``` | ```"🪤"```
```water``` | ```"💧"```
- ```Animal``` (via une propriété ```symbol```) :
valeur | ```String```
--- | ---
```rat``` | ```"🐭"```
```cat``` | ```"🐱"```
```dog``` | ```"🐶"```
```wolf``` | ```"🐺"```
```leopard``` | ```"🐆"```
```tiger``` | ```"🐯"```
```lion``` | ```"🦁"```
```elephant``` | ```"🐘"```
- ```Owner``` (via une propriété ```symbol```) :
valeur | ```String```
--- | ---
```noOne``` | ```" "```
```player1``` | ```"🟡"```
```player2``` | ```"🔴"```
- ```Board``` (via la propriété classique), pour que cela donne par exemple (à peu près) :
```
🌿🦁🟡 🌿 🪤 🪹 🪤 🌿 🌿🐯🟡
🌿 🌿🐶🟡 🌿 🪤 🌿 🌿🐱🟡 🌿
🌿🐭🟡 🌿 🌿🐆🟡 🌿 🌿🐺🟡 🌿 🌿🐘🟡
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿 💧 💧 🌿 💧 💧 🌿
🌿🐘🔴 🌿 🌿🐺🔴 🌿 🌿🐆🔴 🌿 🌿🐭🔴
🌿 🌿🐱🔴 🌿 🪤 🌿 🌿🐶🔴 🌿
🌿🐯🔴 🌿 🪤 🪹 🪤 🌿 🌿🦁🔴
```
c'est-à-dire que chaque cellule sera représentée par : son type, le type de la pièce, la couleur du joueur propriétaire de cette pièce.
## Premier test : Command Line Tool
Pour cette première semaine, nous allons créer un test en lignes de commande (qui s'affichera dans l'interface d'XCode).
Le test demandé est très simple :
=> initialiser une valeur de ```Board``` et l'afficher.
> Note :
> Essayez de refaire la grille de départ (comme ci-dessus).
# Critères d'évaluation
Pour être obtenir les points, vous devez faire valider vos aquis par l'enseignant à l'oral pendant les TP (ou si l'enseignant le propose, lors d'une évaluation écrite).
Cette validation doit avoir lieu avant la fin du TP pour obtenir tous les points. Si la validation a lieu pendant le TP2, une pénalité de 50% est appliquée. Si elle a lieu pendant le TP3, une pénalité de 75% est appliquée. Par la suite, plus aucun point n'est attribué.
**Légende**
symbole | signification
--- | ---
☢️ | si ce critère n'est pas respecté => 0/20
🎬 | évalué à la fin du TP
1⃣ | ctirère de niveau 1 : tant que vous n'avez pas obtenu tous les points sur les critères de niveau 1, les points sur les critères suivants ne sont pas attribués
2⃣ | critère de niveau 2 : tant que vous n'avez pas obtenu tous les points sur les critères de niveau 1 et 2, les points sur les critères de niveau 3 ne sont pas attribués
3⃣ | critère de niveau 3
**Critères**
niveau | description | coeff | pénalités TP2 | pénalités TP3
--- | --- | --- | --- | ---
☢️ | Le dépôt doit être accessible par l'enseignant | ☢️
☢️ | un .gitignore doit exister au premier push | ☢️
🎬 | les *Packages* et le test compilent | 4 | 50% | 75%
🎬 | le test s'exécute sans bug | 4 | 50% | 75%
3⃣ | tous mes projets sont dans le même _workspace_ | 2 | 50% | 75%
1⃣ | j'ai créé un *Package* *Model* | 2 | 50% | 75%
1⃣ | j'ai créé l'enum ```CellType``` | 1 | 50% | 75%
1⃣ | j'ai créé l'enum ```Owner``` | 1 | 50% | 75%
1⃣ | j'ai créé l'enum ```Animal``` | 1 | 50% | 75%
3⃣ | ```Owner``` s'affiche comme demandé sous la forme d'un ```String``` | 1 | 50% | 75%
1⃣ | ```Piece``` possède bien les propriétés demandées en lecture seule non-modifiables | 1 | 50% | 75%
1⃣ | ```Piece``` possède un initialiseur | 2 | 50% | 75%
3⃣ | ```Piece``` est transformable en ```String``` comme demandé | 1 | 50% | 75%
1⃣ | ```Cell``` possède bien les propriétés demandées en lecture seule non-modifiables | 1 | 50% | 75%
1⃣ | ```Cell``` possède bien une propriété ```piece``` pouvant ne pas avoir de valeur | 2 | 50% | 75%
1⃣ | ```Cell``` possède un initialiseur utilisant des valeurs par défaut pour deux paramètres | 2 | 50% | 75%
3⃣ | ```Cell``` est transformable en ```String``` comme demandé | 1 | 50% | 75%
1⃣ | ```Board``` possède bien les propriétés demandées en lecture seule non-modifiables | 1 | 50% | 75%
1⃣ | ```Board``` possède bien un tableau à deux dimensions de cellule | 2 | 50% | 75%
2⃣ | ```Board.grid``` est en lecture seule mais modifiable au sein de ```Board``` | 2 | 50% | 75%
1⃣ | ```Board``` possède un initialiseur permettant d'initialiser toutes ses propriétés | 2 | 50% | 75%
2⃣ | l'initialiseur de ```Board``` vérifie les contraintes et renvoie ```nil``` si nécessaire | 3 | 50% | 75%
3⃣ | j'ai créé un *Package* avec les extensions pour l'affichage en lignes de commande | 2 | 50% | 75%
3⃣ | j'ai créé une extensions pour ```CellType``` | 1 | 50% | 75%
3⃣ | j'ai créé une extensions pour ```Animal``` | 1 | 50% | 75%
3⃣ | j'ai créé une extensions pour ```Owner``` | 1 | 50% | 75%
3⃣ | j'ai créé une extensions pour ```Board``` | 2 | 50% | 75%
2⃣ | j'ai créé une application de type *Command Line Tool* utilisant les deux *Packages* précédents | 2 | 50% | 75%
2⃣ | j'initialise correctement ```Board``` dans le test | 2 | 50% | 75%
3⃣ | j'affiche correctement ```Board``` dans le test | 1 | 50% | 75%
3⃣ | mon dépôt possède un readme qui apporte quelque chose... | 2 | 50% | 75%
3⃣ | mon code est commenté | 1 | 50% | 75%
> Note :
> Les coefficients peuvent être amenés à changer et sont donnés à titre indicatif
---
Copyright &copy; 2023-2024 Marc Chevaldonné
Pendant la préparation de ce TP et de cet énoncé, j'écoutais...
<table>
<tr>
<td>
<img src="./images/listeningto/fuse_one.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Fuse One</b></p>
<p><i>Silk</i> (1981)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/mingus_at_monterey.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Mingus At Monterey</b></p>
<p><i>Charles Mingus</i> (1964)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/upon_the_wings_of_music.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Upon The Wings Of Music</b></p>
<p><i>Jean-Luc Ponty</i> (1975)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/bayou_country.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Bayou Country</b></p>
<p><i>Creedence Clearwater Revival</i> (1969)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/friday_night.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Live At The Old Absinthe House bar ...Friday Night</b></p>
<p><i>Bryan Lee</i> (1997)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/vanguard_potter.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Follow The Red Line - Live At The Village Vanguard</b></p>
<p><i>Chris Potter Underground</i> (2007)</p>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<img src="./images/listeningto/digital_works.jpg" width="120"/>
</td>
<td>
<div>
<p><b>Digital Works</b></p>
<p><i>Ahmad Jamal</i> (1985)</p>
</div>
</td>
</tr>
</table>
Loading…
Cancel
Save