2015-02-10 10:31:31 +01:00
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
TP AP1
Licence SESI 1 ère année
Univ . Lille 1
bataille_navale . py
Module pour le jeu de bataille navale .
Pour une description générale du jeu voir
http : / / fr . wikipedia . org / wiki / Bataille_navale_ % 28 jeu % 29
"""
__author__ = ' BEAUSSART Jean-loup & PREUD \' HOMME Geoffrey '
2015-02-12 19:20:10 +01:00
# TODO Metre à jour avant envoi
__date_creation__ = ' Tue, 10 Feb 2015 11:26:19 +0100 '
2015-02-10 10:31:31 +01:00
###############################################
# Modules utilisés
###############################################
# Pour la disposition aléatoire des navires
from random import randint
# Pour le fichier des scores
2015-02-12 19:20:10 +01:00
from datetime import datetime
2015-02-10 10:31:31 +01:00
###############################################
# Constantes nommées utilisées
###############################################
# les deux dispositions possibles pour un navire
# sur le plateau :
# - H : horizontale
# - V : verticale
2015-02-12 19:20:10 +01:00
DISPOSITIONS = " HV "
2015-02-10 10:31:31 +01:00
# codes réponses d'analyse de tirs
RATE = 0
TOUCHE = 1
COULE = 2
# nom du fichier contenant les scores réalisés
FICHIER_RESULT = ' bataille_navale_scores.txt '
###############################################
# Procédure principale du jeu
###############################################
2015-02-12 19:20:10 +01:00
def jouer ( nom , descr ) :
2015-02-10 10:31:31 +01:00
"""
str , str - > ( )
procédure de jeu complet de bataille navale ,
2015-02-12 19:20:10 +01:00
le nom du joueur est donné par le paramètre nom ,
2015-02-10 10:31:31 +01:00
et le jeu est décrit dans le fichier descr .
CU : le fichier jeu doit exister et être conforme
à un fichier de description .
"""
jeu = cree_jeu ( descr )
decrire_le_jeu ( jeu )
nbre_tirs = 0
while not tous_coules ( jeu ) :
2015-02-12 19:20:10 +01:00
tir = lire_un_tir ( nom )
2015-02-10 10:31:31 +01:00
nbre_tirs + = 1
2015-02-12 19:20:10 +01:00
nav , res = analyse_un_tir ( jeu , tir )
2015-02-10 10:31:31 +01:00
if res == RATE :
2015-02-12 19:20:10 +01:00
print ( " raté. " )
2015-02-10 10:31:31 +01:00
elif res == TOUCHE :
2015-02-12 19:20:10 +01:00
print ( nav + " touché. " )
2015-02-10 10:31:31 +01:00
else :
2015-02-12 19:20:10 +01:00
print ( nav + " coulé. " )
sauver_result ( nom , descr , nbre_tirs )
print ( " Terminé en {0} tirs " . format ( nbre_tirs ) )
2015-02-10 10:31:31 +01:00
###############################################
# Opérations sur les fichiers
###############################################
2015-02-12 19:20:10 +01:00
2015-02-10 10:31:31 +01:00
def lire_donnees ( num_descr ) :
"""
str - > tuple
2015-02-12 19:20:10 +01:00
renvoie un triplet dont les deux premières composantes sont
2015-02-10 10:31:31 +01:00
et la troisième une liste de couples ( nature , taille ) où
nature est une chaîne de caractères décrivant la nature du navire
et taille un entier désignant le nombre de cases occupées par ce navire .
Toutes ces données sont lues dans un fichier nommé ' jeu ' + num_descr + ' .txt ' .
2015-02-12 19:20:10 +01:00
CU : le fichier ' jeu ' + num_descr + ' .txt ' doit exister et être au bon format ,
2015-02-10 10:31:31 +01:00
ie un fichier texte contenant :
larg : haut
nature1 : taille1
nature2 : taille2
. . .
"""
2015-02-13 21:54:59 +01:00
# with s'occupe de fermer automatiquement le fichier ouvert, c'est un gestionnaire de contexte. Il ferme le fichier à la fin du bloc, si une exception est levée ou si on rencontre un break/return
with open ( ' jeu %s .txt ' % num_descr , ' r ' ) as fichier :
dimensions = fichier . readline ( ) . split ( ' : ' )
largeur , hauteur = int ( dimensions [ 0 ] . strip ( ) ) , int ( dimensions [ 1 ] . strip ( ) )
2015-02-10 10:31:31 +01:00
2015-02-13 21:54:59 +01:00
navires = list ( )
2015-02-10 11:08:04 +01:00
2015-02-13 21:54:59 +01:00
for ligne in fichier :
couple = ligne . split ( ' : ' )
navires . append ( ( couple [ 0 ] . strip ( ) , int ( couple [ 1 ] . strip ( ) ) ) )
2015-02-10 11:08:04 +01:00
2015-02-13 21:54:59 +01:00
return ( largeur , hauteur , navires )
2015-02-10 11:08:04 +01:00
2015-02-12 19:20:10 +01:00
# test OK
# print(lire_donnees('2'))
# print(lire_donnees('1'))
#
2015-02-10 11:12:39 +01:00
2015-02-12 19:20:10 +01:00
# Fichier personnalisé
# print(lire_donnees('3'))
2015-02-10 11:12:39 +01:00
#
2015-02-10 11:35:56 +01:00
# Sauvegarde du bilan
2015-02-13 21:54:59 +01:00
# Question 1
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
# print(datetime.today())
# 2015-02-10 11:25:48.324981
# On obtient la date et l'heure au format AAAA-MM-JJ HH:MM:S
2015-02-10 11:35:56 +01:00
2015-02-12 19:20:10 +01:00
# str(datetime.today())
# on convertie en chaîne de caractères la date et l'heure
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
def sauver_result ( nom , jeu , nbre ) :
2015-02-10 10:31:31 +01:00
"""
str , str , int - > NoneType
ajoute une ligne dans le fichier FICHIER_RESULT
2015-02-12 19:20:10 +01:00
contenant le nom , le numéro du jeu joué et le nombre de tirs effectués
2015-02-10 10:31:31 +01:00
dans la partie .
CU : aucune
"""
2015-02-10 11:35:56 +01:00
date = str ( datetime . today ( ) )
2015-02-13 21:54:59 +01:00
with open ( FICHIER_RESULT , ' a ' ) as fichierScores :
fichierScores . write ( ' %s : %s : %s : %s \n ' % ( nom , jeu , nbre , date ) )
2015-02-10 10:31:31 +01:00
2015-02-10 11:40:19 +01:00
# sauver_result ('giouog', 2, 125)
# sauver_result ('nom', 52, 12)
# sauver_result ('gei', 1112, 12356)
# sauver_result ('fgt', 12, 1458)
2015-02-10 10:31:31 +01:00
###############################################
# Procédures de construction du jeu
###############################################
2015-02-12 19:20:10 +01:00
def cree_jeu ( descr ) :
2015-02-10 10:31:31 +01:00
"""
str - > dict
2015-02-12 19:20:10 +01:00
renvoie un nouveau jeu de bataille navale construit à partir des données
2015-02-10 10:31:31 +01:00
lues dans le fichier descr .
Le jeu est représenté par un dictionnaire à quatre clés :
- ' plateau ' pour représenter le plateau de jeu ( l ' espace maritime) avec ses navires
- ' nb_cases_occupees ' dont la valeur associée est le nombre de cases du plateau
occupées par les navires
- ' touches ' dictionnaire contenant deux champs :
2015-02-12 19:20:10 +01:00
* l ' un nomme ' nb_touches ' contenant un entier
2015-02-10 10:31:31 +01:00
* l ' autre nomme ' etats_navires ' qui contient un dictionnaire
2015-02-12 19:20:10 +01:00
donnant pour chaque navire le nombre de tirs
2015-02-10 10:31:31 +01:00
qu ' ils peuvent encore recevoir avant d ' être coulés
- ' coups_joues ' ensemble des coups joués depuis le début de la partie .
CU : le fichier doit contenir une description correcte du jeu ( cf lire_donnees )
"""
2015-02-12 19:20:10 +01:00
def cree_plateau ( l , h , l_nav ) :
2015-02-10 10:31:31 +01:00
"""
int , int , list - > dict
2015-02-12 19:20:10 +01:00
renvoie un plateau de largeur l et de hauteur h occupé par les navires
2015-02-10 10:31:31 +01:00
de l_nav .
La disposition est aléatoire .
CU : les dimensions doivent permettre le placement de tous les navires
"""
2015-02-13 19:11:44 +01:00
esp = { ' larg ' : l , ' haut ' : h }
for nav in l_nav :
placer ( esp , nav )
2015-02-13 19:36:52 +01:00
return esp
2015-02-10 10:31:31 +01:00
2015-02-13 19:09:37 +01:00
def cases_du_navire ( nav , pos , disp ) :
2015-02-13 18:56:03 +01:00
"""
2015-02-13 19:09:37 +01:00
tuple , tuple , str - > list
fournit une liste des cases occupées par un navire nav
aux coordonnées pos dans la disposition disp
CU : disp = ' H ' ou ' V '
2015-02-13 18:56:03 +01:00
"""
2015-02-13 19:09:37 +01:00
assert ( disp in DISPOSITIONS ) , ' disp = \' H \' ou \' V \' '
2015-02-13 18:56:03 +01:00
2015-02-13 19:09:37 +01:00
cases = list ( )
if disp == ' H ' :
for i in range ( pos [ 0 ] , pos [ 0 ] + nav [ 1 ] ) :
cases . append ( ( i , pos [ 1 ] ) )
elif disp == ' V ' :
for i in range ( pos [ 1 ] , pos [ 1 ] + nav [ 1 ] ) :
cases . append ( ( pos [ 0 ] , i ) )
return cases
2015-02-12 19:20:10 +01:00
def est_placable ( esp , nav , pos , disp ) :
2015-02-10 10:31:31 +01:00
"""
dict , tuple , tuple , str - > bool
2015-02-13 19:09:37 +01:00
indique si le navire nav aux coordonnées pos peut être posé dans la
disposition disp dans l ' espace maritime esp
2015-02-10 10:31:31 +01:00
CU : disp = ' H ' ou ' V '
"""
2015-02-13 18:56:03 +01:00
assert ( disp in DISPOSITIONS ) , ' disp = \' H \' ou \' V \' '
2015-02-13 19:09:37 +01:00
for c in cases_du_navire ( nav , pos , disp ) :
if c [ 0 ] > esp [ ' larg ' ] or c [ 0 ] < = 0 or c [ 1 ] < = 0 or c [ 1 ] > esp [ ' haut ' ] or c in esp :
return False
2015-02-13 18:56:03 +01:00
return True
# Tests
# espTest = {'larg': 4, 'haut': 3, (1, 2): 'grand', (2, 2): 'grand', (4, 1): 'petit'}
2015-02-13 19:09:37 +01:00
# print(est_placable(espTest, ('Poutre', 3), (1, 1), 'H')) # True
# print(est_placable(espTest, ('Poutre', 3), (1, 1), 'V')) # False
# print(est_placable(espTest, ('Poutre', 3), (3, 1), 'V')) # True
# print(est_placable(espTest, ('Poutre', 3), (3, 2), 'V')) # False
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
def placer ( esp , nav ) :
2015-02-10 10:31:31 +01:00
"""
dict , tuple - > NoneType
place le navire nav dans l ' espace maritime esp.
Choix de l ' emplacement aléatoire.
CU : il doit rester de la place
"""
2015-02-13 19:09:37 +01:00
while True :
pos = ( randint ( 0 , esp [ ' larg ' ] - 1 ) , randint ( 0 , esp [ ' haut ' ] - 1 ) )
disp = DISPOSITIONS [ randint ( 0 , 1 ) ]
if est_placable ( esp , nav , pos , disp ) :
for c in cases_du_navire ( nav , pos , disp ) :
esp [ c ] = nav [ 0 ]
2015-02-13 19:37:39 +01:00
return
2015-02-13 19:09:37 +01:00
# Test
# espTest2 = {'larg': 4, 'haut': 3}
# placer(espTest2, ('grand', 2))
# placer(espTest2, ('petit', 1))
# print(espTest2) # Aléatoire
2015-02-10 10:31:31 +01:00
###############################################
# Procédures de déroulement du jeu
###############################################
2015-02-12 19:20:10 +01:00
def decrire_le_jeu ( jeu ) :
2015-02-10 10:31:31 +01:00
"""
dict - > ( )
imprime une description du jeu .
2015-02-12 19:20:10 +01:00
2015-02-10 10:31:31 +01:00
CU : aucune
"""
2015-02-10 11:44:31 +01:00
print ( ' Dimensions du plateau de jeu : ' )
print ( ' - largeur : %d ' % jeu [ ' plateau ' ] [ ' larg ' ] )
print ( ' - hauteur : %d ' % jeu [ ' plateau ' ] [ ' haut ' ] )
2015-02-12 19:20:10 +01:00
2015-02-10 11:44:31 +01:00
print ( ' Navires : ' )
etats = jeu [ ' touches ' ] [ ' etats_navires ' ]
for navire in etats :
2015-02-12 19:20:10 +01:00
print ( ' - %s : %d case %s ' %
( navire , etats [ navire ] , ' s ' * ( etats [ navire ] > = 2 ) ) )
print (
' À vous de jouer en répondant à l \' invite ?- par deux nombres séparés par une virgule. ' )
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
def lire_un_tir ( nom ) :
2015-02-10 10:31:31 +01:00
"""
str - > tuple
renvoie un couple d ' entiers lus sur l ' entrée standard
CU : l ' entrée doit être de la forme xxx,yyy avec xxx et yyy
une représentation décimale de deux nombres entiers
"""
2015-02-10 13:18:07 +01:00
while True :
try :
2015-02-12 19:20:10 +01:00
entree = input ( ) # TODO Texte
2015-02-10 13:18:07 +01:00
dec = entree . split ( ' , ' )
x , y = int ( dec [ 0 ] ) , int ( dec [ 1 ] )
if type ( x ) == type ( y ) == int and x > = 0 and y > = 0 :
return ( x , y )
else :
raise ValueError
2015-02-13 19:28:04 +01:00
except KeyboardInterrupt :
sys . exit ( 0 )
2015-02-12 19:20:10 +01:00
except : # TODO Tester erreurs possibles
2015-02-10 13:18:07 +01:00
print ( ' Merci de saisir une valeur correcte. ' )
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
def analyse_un_tir ( jeu , tir ) :
2015-02-10 10:31:31 +01:00
"""
dict , tuple - > str , int
2015-02-12 19:20:10 +01:00
renvoie
2015-02-10 10:31:31 +01:00
- ( " " , RATE ) si tir raté
- ( nav , TOUCHE ) si nav touché
- ( nav , COULE ) si nav coulé
et modifie l ' état du jeu en conséquence.
2015-02-12 19:20:10 +01:00
CU : aucune
2015-02-10 10:31:31 +01:00
"""
2015-02-12 19:06:52 +01:00
if tir in jeu [ ' coups_joues ' ] :
print ( ' DEBUG Vous avez déjà joué à cet endroit. ' )
2015-02-12 19:20:10 +01:00
return ( ' ' , RATE )
2015-02-12 19:06:52 +01:00
elif tir in jeu [ ' plateau ' ] :
2015-02-13 19:45:55 +01:00
jeu [ " coups_joues " ] . add ( tir )
2015-02-12 19:06:52 +01:00
nav = jeu [ ' plateau ' ] [ tir ]
jeu [ ' touches ' ] [ ' nb_touches ' ] + = 1
jeu [ ' touches ' ] [ ' etats_navires ' ] [ nav ] + = - 1
if jeu [ ' touches ' ] [ ' etats_navires ' ] [ nav ] > 0 :
return ( nav , TOUCHE )
else :
return ( nav , COULE )
else :
2015-02-13 19:45:55 +01:00
jeu [ " coups_joues " ] . add ( tir )
2015-02-12 19:06:52 +01:00
return ( ' ' , RATE )
2015-02-10 10:31:31 +01:00
2015-02-12 19:20:10 +01:00
def tous_coules ( jeu ) :
2015-02-10 10:31:31 +01:00
"""
dict - > bool
renvoie True si tous les navires du plateau de jeu ont été coulés
et False dans le cas contraire .
CU : aucune
"""
2015-02-12 19:16:04 +01:00
# TODO Vérifier si les etats_navires sont tous à 0, agir en conséquence ?
return jeu [ ' touches ' ] [ ' nb_touches ' ] > = jeu [ ' nb_cases_occupees ' ]
2015-02-10 10:31:31 +01:00
###############################################
# Pour une utilisation du module depuis un terminal
2015-02-12 19:20:10 +01:00
###############################################
2015-02-10 10:31:31 +01:00
# if __name__ == '__main__':
# import sys
# if len (sys.argv) != 3:
# jouer ('Jean Bart','1')
# else:
# jouer (sys.argv[1],sys.argv[2])