How to abstract over two similar functions - python

I have the following data definition about a football game:
Game = namedtuple('Game', ['Date', 'Home', 'Away', 'HomeShots', 'AwayShots',
'HomeBT', 'AwayBT', 'HomeCrosses', 'AwayCrosses',
'HomeCorners', 'AwayCorners', 'HomeGoals',
'AwayGoals', 'HomeXG', 'AwayXG'])
Here are some exmaples:
[Game(Date=datetime.date(2018, 10, 21), Home='Everton', Away='Crystal Palace', HomeShots='21', AwayShots='6', HomeBT='22', AwayBT='13', HomeCrosses='21', AwayCrosses='14', HomeCorners='10', AwayCorners='5', HomeGoals='2', AwayGoals='0', HomeXG='1.93', AwayXG='1.5'),
Game(Date=datetime.date(2019, 2, 27), Home='Man City', Away='West Ham', HomeShots='20', AwayShots='2', HomeBT='51', AwayBT='6', HomeCrosses='34', AwayCrosses='5', HomeCorners='12', AwayCorners='2', HomeGoals='1', AwayGoals='0', HomeXG='3.68', AwayXG='0.4'),
Game(Date=datetime.date(2019, 2, 9), Home='Fulham', Away='Man Utd', HomeShots='12', AwayShots='15', HomeBT='19', AwayBT='38', HomeCrosses='20', AwayCrosses='12', HomeCorners='5', AwayCorners='4', HomeGoals='0', AwayGoals='3', HomeXG='2.19', AwayXG='2.13'),
Game(Date=datetime.date(2019, 3, 9), Home='Southampton', Away='Tottenham', HomeShots='12', AwayShots='15', HomeBT='13', AwayBT='17', HomeCrosses='15', AwayCrosses='15', HomeCorners='1', AwayCorners='10', HomeGoals='2', AwayGoals='1', HomeXG='2.08', AwayXG='1.27'),
Game(Date=datetime.date(2018, 9, 22), Home='Man Utd', Away='Wolverhampton', HomeShots='16', AwayShots='11', HomeBT='17', AwayBT='17', HomeCrosses='26', AwayCrosses='13', HomeCorners='5', AwayCorners='4', HomeGoals='1', AwayGoals='1', HomeXG='0.62', AwayXG='1.12')]
And two almost identical functions calculating home and away statistics for a given team.
def calculate_home_stats(team, games):
"""
Calculates home stats for the given team.
"""
home_stats = defaultdict(float)
home_stats['HomeShotsFor'] = sum(int(game.HomeShots) for game in games if game.Home == team)
home_stats['HomeShotsAgainst'] = sum(int(game.AwayShots) for game in games if game.Home == team)
home_stats['HomeBoxTouchesFor'] = sum(int(game.HomeBT) for game in games if game.Home == team)
home_stats['HomeBoxTouchesAgainst'] = sum(int(game.AwayBT) for game in games if game.Home == team)
home_stats['HomeCrossesFor'] = sum(int(game.HomeCrosses) for game in games if game.Home == team)
home_stats['HomeCrossesAgainst'] = sum(int(game.AwayCrosses) for game in games if game.Home == team)
home_stats['HomeCornersFor'] = sum(int(game.HomeCorners) for game in games if game.Home == team)
home_stats['HomeCornersAgainst'] = sum(int(game.AwayCorners) for game in games if game.Home == team)
home_stats['HomeGoalsFor'] = sum(int(game.HomeGoals) for game in games if game.Home == team)
home_stats['HomeGoalsAgainst'] = sum(int(game.AwayGoals) for game in games if game.Home == team)
home_stats['HomeXGoalsFor'] = sum(float(game.HomeXG) for game in games if game.Home == team)
home_stats['HomeXGoalsAgainst'] = sum(float(game.AwayXG) for game in games if game.Home == team)
home_stats['HomeGames'] = sum(1 for game in games if game.Home == team)
return home_stats
def calculate_away_stats(team, games):
"""
Calculates away stats for the given team.
"""
away_stats = defaultdict(float)
away_stats['AwayShotsFor'] = sum(int(game.AwayShots) for game in games if game.Away == team)
away_stats['AwayShotsAgainst'] = sum(int(game.HomeShots) for game in games if game.Away == team)
away_stats['AwayBoxTouchesFor'] = sum(int(game.AwayBT) for game in games if game.Away == team)
away_stats['AwayBoxTouchesAgainst'] = sum(int(game.HomeBT) for game in games if game.Away == team)
away_stats['AwayCrossesFor'] = sum(int(game.AwayCrosses) for game in games if game.Away == team)
away_stats['AwayCrossesAgainst'] = sum(int(game.HomeCrosses) for game in games if game.Away == team)
away_stats['AwayCornersFor'] = sum(int(game.AwayCorners) for game in games if game.Away == team)
away_stats['AwayCornersAgainst'] = sum(int(game.HomeCorners) for game in games if game.Away == team)
away_stats['AwayGoalsFor'] = sum(int(game.AwayGoals) for game in games if game.Away == team)
away_stats['AwayGoalsAgainst'] = sum(int(game.HomeGoals) for game in games if game.Away == team)
away_stats['AwayXGoalsFor'] = sum(float(game.AwayXG) for game in games if game.Away == team)
away_stats['AwayXGoalsAgainst'] = sum(float(game.HomeXG) for game in games if game.Away == team)
away_stats['AwayGames'] = sum(1 for game in games if game.Away == team)
return away_stats
I'm wondering if there is a way to abstract over these two functions and merge them into one without creating a wall of if/else statements to determine whether the team plays at home or away from home and which fields should be counted.

Having cleaner data structure allow for writing simpler code.
In that case, your data already contains duplication
(eg, you have both HomeShots and AwayShots).
There are many possible answers to how you could structure data here.
I'll just go over a solution that doesn't change too much from
your original structure.
Statistics = namedtuple('Statistics', ['shots', 'BT', 'crosses', 'corners', 'goals', 'XG'])
Game = namedtuple('Game', ['home', 'away', 'date', 'home_stats', 'away_stats'])
You could use this like this (I haven't included all stats here, just a few to give an example):
def calculate_stats(games, team_name, home_stats_only=False, away_stats_only=False):
home_stats = [g.home_stats._asdict() for g in games if g.home == team_name]
away_stats = [g.away_stats._asdict() for g in games if g.away == team_name]
if away_stats_only:
input_stats = away_stats
elif home_stats_only:
input_stats = home_stats
else:
input_stats = home_stats + away_stats
def sum_on_field(field_name):
return sum(stats[field_name] for stats in input_stats)
return {f:sum_on_field(f) for f in Statistics._fields}
Which can then be used to get both away/home stats:
example_game_1 = Game(
home='Burnley',
away='Arsenal',
date=datetime.now(),
home_stats=Statistics(shots=12, BT=26, crosses=21, corners=4, goals=1, XG=1.73),
away_stats=Statistics(shots=17, BT=26, crosses=22, corners=5, goals=3, XG=2.87),
)
example_game_2 = Game(
home='Arsenal',
away='Pessac',
date=datetime.now(),
home_stats=Statistics(shots=1, BT=1, crosses=1, corners=1, goals=1, XG=1),
away_stats=Statistics(shots=2, BT=2, crosses=2, corners=2, goals=2, XG=2),
)
print(calculate_stats([example_game_1, example_game_2], 'Arsenal'))
print(calculate_stats([example_game_1, example_game_2], 'Arsenal', home_stats_only=True))
print(calculate_stats([example_game_1, example_game_2], 'Arsenal', away_stats_only=True))
Which prints:
{'shots': 18, 'BT': 27, 'crosses': 23, 'corners': 6, 'goals': 4, 'XG': 3.87}
{'shots': 1, 'BT': 1, 'crosses': 1, 'corners': 1, 'goals': 1, 'XG': 1}
{'shots': 17, 'BT': 26, 'crosses': 22, 'corners': 5, 'goals': 3, 'XG': 2.87}
When dealing with this kind of data, it's usually a good idea to use specialised tools like, for example, pandas. It could also be very convenient to use interactive tools, like JupyterLab.

I recommend not using a named tuple but a simple tuple with a dictionary, for example:
game=(datetime.date(2019, 5, 12), 'Burnley', 'Arsenal', '12', '17', '26', '26', '21', '22', '4', '5', '1', '3', '1.73', '2.87')
And a mapping dictionary:
numtostr={0: 'Date', 1: 'Home', 2: 'Away', 3: 'HomeShots', 4: 'AwayShots', 5: 'HomeBT', 6: 'AwayBT', 7: 'HomeCrosses', 8: 'AwayCrosses', 9: 'HomeCorners', 10: 'AwayCorners', 11: 'HomeGoals', 12: 'AwayGoals', 13: 'HomeXG'}
strtonum={'Date': 0, 'Home': 1, 'Away': 2, 'HomeShots': 3, 'AwayShots': 4, 'HomeBT': 5, 'AwayBT': 6, 'HomeCrosses': 7, 'AwayCrosses': 8, 'HomeCorners': 9, 'AwayCorners': 10, 'HomeGoals': 11, 'AwayGoals': 12, 'HomeXG': 13}
Make the mapping dictionaries for homestats and awaystats ({0: 'HomeShotsFor', 1: 'HomeShotsAgainst' etc} for home_stats). To explain how mapping dictionaries work, for example, if you want to get the HomeCrosses of a game, you can have
game[7]
or
game[strtonum['HomeCrosses']]
Then the functions:
def calculate_home_stats(team, games):
home_stats=[0]*13
for game in games:
if game[1]=team:
for index in range(12):
home_stats[index]+=game[index+3] #because you just put the sum of everything except date, home, and away which are the first 3 indices. see how this cleans everything up?
home_stats[12]+=1
def calculate_away_stats(team, games):
away_stats=[0]*13
for game in games:
if game[2]=team:
for index in range(12):
away_stats[index]+=game[index+3]
away_stats[12]+=1
If you really want to merge both functions into one you can do this:
def calculate_stats(team, games, homeaway):
stats=[0]*13
for game in games:
if game[{'Home': 1, 'Away': 2}[homeaway]]=team:
for index in range(12):
stats[index]+=game[index+3]
stats[12]+=1
As with my function the only thing you have to change is the index to check for home or away, instead of the redundant if else statements which require a lot of change.

Related

How to assign list of teams to a list of users randomly in python

For example:
Team
User
USA
Mark
England
Sean
India
Sri
assigning users to different teams randomly
You could use shuffle to shuffle a user list;
from random import shuffle
teams = ['USA', 'England', 'India']
users = ['Mark', 'Sean', 'Sri']
shuffle(users)
print([(t,u) for t,u in zip(teams, users)])
To assign multiple teams to a player, you can use iter() to ensure there are no duplicates
from random import shuffle
teams = ['USA', 'England', 'India','France', 'Brazil', 'Australia']
users = ['Mark', 'Sean', 'Sri']
shuffle(teams)
teams_iter = iter(teams)
print([(u,(t1,t2)) for u,t1,t2 in zip(users, teams_iter, teams_iter )])
In random module use choice
from random import choice
choice(['USA','England','India'])
'India'
For a dataframe of users you could use lambda to get a random choice for each user:
df.apply (lambda x: choice(['USA','England','India']))
Here a posible solution
from random import randrange
User = ['Mark', 'Sean', 'Sri']
Team = ['USA', 'England', 'India']
_range = len(User)
new_list = []
while len(new_list) != _range:
try:
rr = randrange( len(Team) );
new_list.append( [Team.pop(), User.pop( rr ) ] )
except:
""
print(new_list)

Including another object inside an object and calling a specific value

I'm trying to make a simple 5v5 sport management, but I'm not sure how can I link players to teams, in order to make the overall rating of the team be the sum of the players rating / amount of players. Unfortunately my current code only returns where the object is ( <main.Player object at 0x000001CED897FCD0>) but not the player at all, so of course the overall is off
class Player:
totalPlayers = 0
def __init__(self,PName, PYearBorn, POvr, PNumber, PIndex):
self.PName = PName
self.PYearBorn = PYearBorn
self.POvr = POvr
self.PNumber = PNumber
self.PIndex = PIndex
Player.totalPlayers += 1
def getPlayer(self):
return self.PName, self.PYearBorn, self.POvr, self.PNumber, self.PIndex
'''
Player Name
Year Born
Player Overall
Player Number
Player Index'''
class HistoricPlayer(Player):
def __init__(self,PName, PlayerAge, POvr, PNumber, PIndex):
self.PlayerAge=PlayerAge
Player.__init__(self,PName, PlayerAge, POvr, PNumber, PIndex,)
class Franchise():
totalFranchises = 0
def __init__(self, FName, region, championships, yearCreated, rivals):
self.FName = FName
self.region = region
self.championships = championships
self.yearCreated = yearCreated
self.rivals = rivals
Franchise.totalFranchises += 1
class Team(Franchise):
totalTeams = 0
def __init__(self, TName, FName, amountofplayers, maxplayercapacity, region, championships, yearCreated, rivals, currentplayers):
Franchise.__init__(self, FName, region, championships, yearCreated, currentplayers)
self.currentplayers = currentplayers
self.overall = currentplayers[1].POvr/amountofplayers
self.rivals = rivals
self.TName = TName
self.amountofplayers = amountofplayers
self.maxplayercapacity = maxplayercapacity
self.currentplayers = currentplayers
Team.totalTeams+=1
def getTeam(self):
return self.TName, self.FName, self.amountofplayers, self.maxplayercapacity, self.region, self.championships, self.yearCreated, self.rivals, self.currentplayers
''' #Team Class Values
Team Name
Franchise Name
Overall
Amount of current players
Maximum player capacity
Team Region
Championships
Year of Creation
Rivals
Current Players
'''
P01=Player('Francis', 2000, 8, 69, 'p01')
P02=Player('Franton', 2000, 8, 69, 'p01')
P03=Player('Frank', 2000, 8, 69, 'p01')
P04=Player('Felitio', 2000, 8, 69, 'p01')
P05=Player('Fred', 2000, 8, 69, 'p01')
T01=Team('5 Friends', "The friends' club", 5, 6, "Hometown", 0, 2022, 'Rich', (P01, P02, P03, P04, P05))
print(T01.getTeam())
Any idea what should or can I do/what am I doing wrong?

Retrieve specific fields of a namedtuple instance from a nested list in Python

I am starting a project for school, a basic text-adventure game, where the player navigates through several rooms, each with a set of items, and adds an item to their inventory by spending a turn.
Since each item is unique to a room and each item is also unique in its stat benefits, I have done the following to implement these:
Item = namedtuple('Item', ['name', 'atk', 'defense', 'agi', 'hp'])
#Sport Store Items
bat = Item('Baseball Bat', 3, 1, 0, 0)
shoes = Item('Running Shoes', 0, 1, 3, 0)
dryeggs = Item('Freeze Dried Eggs', 0, 0, 1, 2)
#Clothes Store Items
belt = Item('Studded Belt', 1, 1, 0, 0)
candy = Item('Japanese Candy', 0, 0, 1, 1)
jacket = Item('Leather Jacket', 0, 3, 0, 1)
#Toy Store Items:
cars = Item('Toy Car Pack', 1, 1, 0, 0)
crayons = Item('Crayons', 0, 0, 1, 0)
toygun = Item('Toy Gun', 2, 1, 0, 0)
#Candle Store Items:
jar = Item('Candle Jar', 2, 0, 0, 0)
matches = Item('Matches', 1, 0, 1, 0)
wax = Item('Wax', 0, 2, 1, 0)
#Music Store Items:
disc = Item('Vinyl Disc', 2, 0, 1, 0)
guitar = Item('Electric Guitar', 3, 0, 0, 0)
symbol = Item('Symbol', 1, 2, 0, 0)
all_items = [
[bat, shoes, dryeggs, '1'],
[belt, candy, jacket, '2'],
[cars, crayons, toygun, '3'],
[jar, matches, wax, '4'],
[disc, guitar, symbol, '5']
]
My issue is here, in my get_items function:
def get_items(id):
for i in all_items:
if i[3] == id:
items = i
items = list(items[0:4])
return items
else:
continue
I'm trying to get the list of items based on the matching ID parameter. There is a function in another file that takes the player's current position and sends it to other functions as the map_id. I've successfully made it so the name of the store changes based on this position, but I cannot figure out how to handle these sub-lists to return a specific sub-list based on the id.
For example, in the sport_store code, I'm trying to return the results like so:
def sport_store():
room_id = '1'
item_select = items.get_items(room_id)
#FIXME: import items.py later with this function in it.
if item_select != []:
if entering == True:
print('You enter the Sporting Goods Store.')
print('There aren\'t many supplies left. What will you pick?')
else:
print('There aren\'t many supplies left. What will you pick?')
print(item_select)
However, no matter what things I change in get_items' loop to get it to work, I always return the original empty list of item_selection. I am trying to select the matching sub-list from the all_items global with the get_items function and then use that sub-list to make a new list that gets sent to item_selection showing the items for that store, and then format the namedtuples in it to show just the names value. Is this possible or do I need to convert the data to a dictionary?
you can use a dictionary for all items:
all_items = {
'1':[bat, shoes, dryeggs],
'2':[belt, candy, jacket],
'3':[cars, crayons, toygun],
'4':[jar, matches, wax],
'5':[disc, guitar, symbol]
}
then, instead of calling items.getItems(room_id), you could just do all_items[room_id]. Hope this helps!

how to get the same output by destructing in python

I want to get the same output as in the first code with the second code but with a slight variation. I want to de-structure the list "players" and then get the same output.
Here is the first code:
import random
lottery_numbers = set(random.sample(range(22), 6))
players = [
{"name": "Rolf", "numbers": {1, 3, 5, 7, 9, 11}},
{"name": "Charlie", "numbers": {2, 7, 9, 22, 10, 5}},
{"name": "Anna", "numbers": {13, 14, 15, 16, 17, 18}},
{"name": "Jen", "numbers": {19, 20, 12, 7, 3, 5}}
]
top_player = players[0]
for player in players:
matched_numbers = len(player["numbers"].intersection(lottery_numbers))
if matched_numbers > len(
top_player["numbers"].intersection(lottery_numbers)):
top_player = player
print(top_player)
I want to de-structure the list "players" to "name" and "player" and then compare the variable "player" with the numbers that matched with lottery_numbers
Here is the second piece of code:
for name, player in players[1].items():
matched_numbers = len(player.intersection(lottery_numbers))
if matched_numbers > len(
top_player["numbers"].intersection(lottery_numbers)):
top_player = player
print(top_player)
Pycharm seems to hit me with a error like this:
in matched_numbers = len(player.intersection(lottery_numbers))
AttributeError: 'str' object has no attribute 'intersection'
PS: I am pretty new with python and don't know much about what the error evens means..
The first thing you are doing is saying players[1].items(), this would return:
{"name": "Charlie", "numbers": {2, 7, 9, 22, 10, 5}}
because that is the first index of the players list. If you want to get the list of names, you can just do something like this:
for player in players:
this will go through all the players and the lottery numbers would just be player["numbers"]
So your final code would look like:
for player in players:
matched_numbers = len(player["numbers"].intersection(lottery_numbers))
if matched_numbers > len(
top_player["numbers"].intersection(lottery_numbers)):
top_player = player
print(top_player)
If you want to split this into 2 different variables, you can use this:
names = []
numbers = []
for player in players:
names.append(player["name"])
numbers.append(player["numbers"])
finalList = zip(names, numbers)
then do:
for name, player in finalList
A few changes to make, to do with how the players list is organised, ie. it's not a dict but rather a list.
import random
lottery_numbers = set(random.sample(range(22), 6))
players = [
{"name": "Rolf", "numbers": {1, 3, 5, 7, 9, 11}},
{"name": "Charlie", "numbers": {2, 7, 9, 22, 10, 5}},
{"name": "Anna", "numbers": {13, 14, 15, 16, 17, 18}},
{"name": "Jen", "numbers": {19, 20, 12, 7, 3, 5}}
]
top_player = players[0]
for player in players:
matched_numbers = len(player["numbers"].intersection(lottery_numbers))
if matched_numbers > len(
top_player["numbers"].intersection(lottery_numbers)):
top_player = player
print(top_player)
for player in players:
name = player["name"]
numbers = player["numbers"]
matched_numbers = len(numbers.intersection(lottery_numbers))
if matched_numbers > len(
top_player["numbers"].intersection(lottery_numbers)):
top_player = player
print(top_player)

Error with code, I am unsure of what the problem is but it seems to be looping

name = ["Hetty", "Poppy", "Blue Skies", "Bay View", "Happy Days", "Summer Joy",
"Walkers Rest", "Bertie","Green Forest Lodge", "Coppice Lodge" ]
cap = [4, 4, 4, 6, 6, 6, 8, 8, 10, 10]
peak = [400, 400, 500, 650, 695, 800, 950, 1050, 1200, 1500]
offpeak = [250, 250, 350, 500, 550, 600, 750, 850, 950, 1150]
onoff = False
cost = 0
print("Here are our avaliable lodges")
for elem in name:
print("- " + elem)
desired_room = (str(input("Enter The Name Of The Room You Would Like To Book: "))).lower()
while True:
for i in range (0,10):
if desired_room == name [i].lower():
print("Name: ", name[i])
print("Capacity: ", cap[i])
print("Off Peak Rate: 0" + str(offpeak[i]))
print("Peak Rate: 0" + str(peak[i]))
exit
print("Invalid Room Name Entered Try Again")
desired_room = (str(input("Enter The Name Of The Room You Would Like To Book: "))).lower()
week = int(input("Enter The Week Your Stay Starts "))
This is my code. When the user inputs the the name of the room it works but then for some reason it loops and asks the user which room they want to book. How can I fix it so "Enter The Week Your Stay Starts " is asked instead of the room that the user wants to book is inputted?
Picture reference
https://imgur.com/a/QAtnHT9
The first problem is that you want break, not exit.
The break statement breaks out of a loop, which is what you want to do.
exit is not a statement. It's the name of a function, which you aren't calling, so it doesn't do anything, any more than just writing sorted would. Also, it isn't intended to be used except in interactive mode; if you need to exit a script in the middle of the script, you use sys.exit(). Also, you don't want to exit the whole script here anyway, just the loop.
But you've got another problem beyond that: You have one loop nested inside another one. Breaking out of the first one won't break out of the second one.
You need to reorganize your logic in some way that it's easy to get out of this code where you want to, and the cleanest way to do that is usually by writing a function, which you can return from:
def book_room():
desired_room = (str(input("Enter The Name Of The Room You Would Like To Book: "))).lower()
while True:
for i in range (0,10):
if desired_room == name [i].lower():
return i
print("Invalid Room Name Entered Try Again")
desired_room = (str(input("Enter The Name Of The Room You Would Like To Book: "))).lower()
And now, you can just call that function:
room = book_room()
print("Name: ", name[room])
print("Capacity: ", cap[room])
print("Off Peak Rate: 0" + str(offpeak[room]))
print("Peak Rate: 0" + str(peak[room]))
While we're at it, you can simplify this code in a number of ways. You can just ask for the input at the top of the loop instead of doing it twice; you don't need to call str on something that's already a string; you can use string formatting instead of manually converting things to strings and concatenating…
def book_room():
while True:
desired_room = input("Enter The Name Of The Room You Would Like To Book: ")).lower()
for i in range(0,10):
if desired_room == name[i].lower():
return i
print("Invalid Room Name Entered Try Again")
room = book_room()
print(f"Name: {name[room]}")
print(f"Capacity: {cap[room]}")
print(f"Off Peak Rate: 0{offpeak[room]}")
print(f"Peak Rate: 0{peak[room]}")
While the points raised in #abarnert's answer are valid, you can use a for-else construct like the following instead to avoid using a separate function to break out of a nested loop:
while True:
for i in range (0,10):
if desired_room == name[i].lower():
print("Name: ", name[i])
print("Capacity: ", cap[i])
print("Off Peak Rate: 0" + str(offpeak[i]))
print("Peak Rate: 0" + str(peak[i]))
break
else:
print("Invalid Room Name Entered Try Again")
desired_room = (str(input("Enter The Name Of The Room You Would Like To Book: "))).lower()
continue
break
Solution
name = [
"Hetty", "Poppy", "Blue Skies", "Bay View", "Happy Days", "Summer Joy",
"Walkers Rest", "Bertie", "Green Forest Lodge", "Coppice Lodge"
]
cap = [4, 4, 4, 6, 6, 6, 8, 8, 10, 10]
peak = [400, 400, 500, 650, 695, 800, 950, 1050, 1200, 1500]
offpeak = [250, 250, 350, 500, 550, 600, 750, 850, 950, 1150]
onoff = False
cost = 0
print("Here are our avaliable lodges")
[print(f"-{elem}") for elem in name]
desired_room = input("Enter The Name Of The Room You Would Like To Book: " \
).title()
while desired_room.title() not in name:
print("Invalid Room Name Entered Try Again")
desired_room = input("Enter The Name Of The Room You Would Like To" +
" Book:").title()
for i in range(len(name)) :
if desired_room == name [i]:
print("Name: ", name[i])
print("Capacity: ", cap[i])
print("Off Peak Rate: 0" + str(offpeak[i]))
print("Peak Rate: 0" + str(peak[i]))
week = int(input("Enter The Week Your Stay Starts "))
It would make your code easier to work with, if you just split what you are trying to do there up a little. If you make a while loop that checks to make sure the desired_room exists first then you can enter your next loop knowing that desired_room is a valid entry.
Also by transforming inputs into title instead of lower you can check with your name quicker since you know all entries are in title case format.
Thoughts
The way you are, you are matching all the indices for each list to get the proper info (name[0], cap[0], peak[0],... give usHetty`'s information). This is fine but what happens when you have 100 rooms and you entry number 67 is off, its going to be rough going through each list and checking index 67. Instead you could use a list of dictionaries to keep each rooms information together. Just trying to give some ideas, threw in some print formatting ideas as well,
def print_info(something):
r_width = len(something['name'])
print("-"*15 + "-"*(r_width+1))
print("Name:".ljust(15), f"{something['name']}".rjust(r_width))
print("Capacity:".ljust(15), f"{something['cap']}".rjust(r_width))
print("Off Peak Rate:".ljust(15), f"{something['offpeak']}".rjust(r_width))
print("Peak Rate:".ljust(15), f"{something['peak']}".rjust(r_width))
print("-"*15 + "-"*(r_width+1))
rooms = [
{'name': 'Hetty', 'cap': 4, 'peak': 400, 'offpeak': 250},
{'name': 'Poppy', 'cap': 4, 'peak': 400, 'offpeak': 250},
{'name': 'Blue Skies', 'cap': 4, 'peak': 500, 'offpeak': 350},
{'name': 'Bay View', 'cap': 6, 'peak': 650, 'offpeak': 500},
{'name': 'Happy Days', 'cap': 6, 'peak': 695, 'offpeak': 550},
{'name': 'Summer Joy', 'cap': 6, 'peak': 800, 'offpeak': 600},
{'name': 'Walkers Rest', 'cap': 8, 'peak': 950, 'offpeak': 750},
{'name': 'Bertie', 'cap': 8, 'peak': 1050, 'offpeak': 850},
{'name': 'Green Forest Lodge', 'cap': 10, 'peak': 1200, 'offpeak': 950},
{'name': 'Coppice Lodge', 'cap': 10, 'peak': 1500, 'offpeak': 1050}
]
onoff = False
cost = 0
room_avail = []
for i in rooms:
room_avail.append(i['name'])
print("Here are our avaliable lodges")
for i in rooms:
print(f"-{i['name']}")
desired_room = input("Enter The Name Of The Room You Would Like To Book: " \
).title()
while desired_room not in room_avail:
print("Invalid Room Name Entered Try Again")
desired_room = input("Enter The Name Of The Room You Would Like To" +
" Book:").title()
for i in rooms:
if desired_room == i['name']:
print_info(i)
week = int(input("Enter The Week Your Stay Starts "))
Output
(xenial)vash#localhost:~/python$ python3.7 hotel.py
Here are our avaliable lodges
-Hetty
-Poppy
-Blue Skies
-Bay View
-Happy Days
-Summer Joy
-Walkers Rest
-Bertie
-Green Forest Lodge
-Coppice Lodge
Enter The Name Of The Room You Would Like To Book: coppice lodge
-----------------------------
Name: Coppice Lodge
Capacity: 10
Off Peak Rate: 1050
Peak Rate: 1500
-----------------------------
Enter The Week Your Stay Starts

Categories

Resources