# Lec 5 Inheritance
'''
# In chess you have different classes of objects. In a broad sense you
have a chess board, and chess pieces.
# All chess pieces have a few things in common:
# Colour: Black or White
# Current Position
# Starting position
# No of turns
# individual chess pieces like rooks, kinghts etc have unique:
# Squares that they threaten
# Valid and invalid moves
# Amongst the rooks there are 4
# chess board will be a class
# chess piece class (parent class)
# Rook class (child class)
# Knight class (child class) etc
# child classes can be thought as a specific type of the parent class
# Object is a specific instance of a class
# 4 rooks, rook_1_white, rook_2_white, rook_1_black, rook_2_black are 4
obkect of the rook class
# animals can be thought of as a parent class
# Mammals, insects, amphibians, fishes can be child classes of animals
# cats, dogs, lions will be child classes of the mammals class
# breeds of dogs can be further child classes
# An alsatian named Timmy can be an object of the Alsatian class
class person:
species = "human being" # class attribute, same for all
instances(objects of this class)
def __init__(self, f_name, l_name, age, gender):
self.f_name = f_name # object attribute, specific to the current
instance
self.l_name = l_name
[Link] = age
[Link] = gender
def print_name(self):
print(self.f_name, self.l_name)
def print_details(self):
print(self.f_name + ' ' + self.l_name + ', is ' + str([Link]) + ' years
old, and is a ' + [Link] )
mike = person("Mike","Cole",65,"male")
mike.print_name()
# Mike Cole
mike.print_details()
# Mike Cole, is 65 years old, and is a male.
# lets create an object that is a type of a person eg. students
# what are some student specific attributes?
# which school
# which standard
# which board
# commerce or science or arts: college students
# for a graduation student: which year, which institute, which stream
class student(person):
pass
# student has inherited all attributes (species) and all methods (__init__,
print_name, print_details) of the person (parent) class.
tejas = student("Tejas", "Gupta", 13, "male")
tejas.print_details()
# Tejas Gupta, is 13 years old, and is a male
print([Link])
# human being
class student(person):
def __init__(self, f_name, l_name, age, gender, school, std, board):
self.f_name = f_name # object attribute, specific to the current
instance
self.l_name = l_name
[Link] = age
[Link] = gender
[Link] = school
[Link] = std
[Link] = board
# overwrite the __init__ method of the parent class.
tejas = student("Tejas", "Gupta", 13, "male","DAV","8th","SSC")
print([Link], [Link])
tejas.print_details()
# 13 HSC
# Tejas Gupta, is 13 years old, and is a male
# syntax to inherit the parent's __init__ method:
class student(person):
def __init__(self, f_name, l_name, age, gender, school, std, board):
# overwritten the parent's __init__ method
person.__init__(self, f_name, l_name, age, gender)
# Copies the parent's __init__() method
[Link] = school
[Link] = std
[Link] = board
print([Link], [Link])
# 13 HSC None
tejas.print_details()
# Tejas Gupta, is 13 years old, and is a male
class student(person):
def __init__(self, f_name, l_name, age, gender, school, std, board):
# overwritten the parent's __init__ method
super().__init__(self, f_name, l_name, age, gender)
# Copies the parent's __init__() method
[Link] = school
[Link] = std
[Link] = board
print([Link], [Link])
# 13 HSC None
tejas.print_details()
# Tejas Gupta, is 13 years old, and is a male
# Create a child of the student class, called college_student:
# he should have one additional attribute called as stream, which may be
arts or science or commerce
# Add a method to the student class called print_edu_details(): which
prints for eg.
# Tejas Gupta is in the 8th std, and studies in DAV. His board is SSC.
# Overwrite the same method in the college_student class, to also print
"Tejas is studying arts"
class student(person):
pronouns = {"male" : 'his', "female" : 'her'}
def __init__(self, f_name, l_name, age, gender, school, std, board):
# overwritten the parent's __init__ method
super().__init__(f_name, l_name, age, gender)
# Copies the parent's __init__() method
[Link] = school
[Link] = std
[Link] = board
def print_edu_details(self):
print(f"{self.f_name} {self.l_name} is in the {[Link]} std, and
studies in {[Link]}. {[Link][[Link]].capitalize()} board is
{[Link]}." )
class college_student(student):
def __init__(self, f_name, l_name, age, gender, school, std, board,
stream):
super().__init__(f_name, l_name, age, gender, school, std, board)
[Link] = stream
def print_edu_details(self):
# Overwrites the parent class's print_edu_details()
super().print_edu_details()
# Copies the parent class' print_edu_details()
print(f"{self.f_name} is studying {[Link]}.")
tejas = student("Tejas", "Gupta", 13, "male","DAV","8th","SSC")
puja = college_student("Puja", "Sharma", 16,
"female","Wadia","11th","HSC", "arts")
tejas.print_edu_details()
puja.print_edu_details()
'''
'''
## 1
class chessboard:
board = []
for i in range (8):
[Link]([])
# appending a new empty row
for j in range (8):
if (i + j) % 2 == 0:
board[i].append(" ")
# represents white
else:
board[i].append("__")
# represents black
'''
‘’’
## 4 (upgarding to keep shape of black and white flexible)
class chessboard:
## 4
black = " X "
white = " O "
## 5a
map = { "a" : 1 , "b" : 2, "c" : 3 , "d": 4, "e" : 5, "f" : 6, "g" : 7, "h" : 8}
## 4
board = []
for i in range (8):
[Link]([])
# appending a new empty row
for j in range (8):
if (i + j) % 2 == 0:
board[i].append(white)
# represents white
else:
board[i].append(black)
# represents black
## 2
def disp(self):
for row in [Link]:
print(row)
print('') # To give a gap
## 5b
# e7 must be column 5, and row 7 from bottom but 2 from the top.
Indexing of the same for python must be 5 -1 = 4, and 2 - 1 = 1. Thus e
becomes 4, and 7 becomes 1.
def get_pos(self, coord):
# fetches the current item or symbol stored in that position
try:
row = 8 - int(coord[1])
col = [Link] [coord[0]] - 1
return [Link][row][col]
except:
print("Position is not valid or not in the right format")
## 5c
def set_pos(self, coord, value):
# Changes item or symbol in the given coordinate, to that contained in
'value'
try:
row = 8 - int(coord[1])
col = [Link] [coord[0]] - 1
[Link][row][col] = value
except:
print("Position is not valid or not in the right format")
## 9
def has_colour(self, coord, colour):
return self.get_pos(coord)[1] == colour
# get_pos(a5) may return Rw1
## 10
def get_row(self, coord):
row = 8 - int(coord[1])
return row
def get_col(self, coord):
col = [Link] [coord[0]] - 1
return col
## 12b
def get_middle_squares(self,old_row,old_col,new_row,new_col):
# Note the shorthand if syntax below
r_step = 0 if old_row == new_row else 1 if old_row < new_row else -1
# if old_row > new_row
c_step = 0 if old_col == new_col else 1 if old_col < new_col else -1 #
if old_col > new_col
list = []
r = old_row + r_step
c = old_col + c_step
while not (r == new_row and c == new_col):
[Link]([r,c])
r += r_step
c += c_step
return list
## 13a
def path_blocked(self,old_row,old_col,new_row,new_col):
list = self.get_middle_squares(old_row,old_col,new_row,new_col)
for x in list:
if [Link][x[0]][x[1]] not in [[Link],[Link]]:
return True
return False
## 16b
def set_original(self,coord):
i = self.get_row(coord)
j = self.get_col(coord)
# white is at [0,0] (top left corner) Black is at [0,1] [1, 0] etc.
if (i + j) % 2 == 0:
[Link][i][j] = [Link]
# represents white
else:
[Link][i][j] = [Link]
# represents black
## 3
cb = chessboard()
[Link]()
## 5d
print (cb.get_pos("h8"))
## 6
class rook:
symbol = 'R'
def __init__ (self,colour,piece_no,start_pos):
[Link] = colour
[Link] = piece_no
[Link] = start_pos
[Link] += colour
[Link] += str(piece_no)
# appending the colour and piece number to the main symbol
## 7a
cb.set_pos([Link],[Link])
# changes item at [Link] aka start_pos to [Link]
## 8 Moving the rook. We need to first check the validity of a move. 1. For
this we need to ensure that the position entered is a new position. 2. We
need to ensure that the position entered is not occupied by our own
colour. Coincidentally check 2 will take care of check 1 as the old position
is already occupied by the same colour (current piece). 3. We need to
ensure all the cells in between are not occupied.
# for check 2 let's make a method for the chessboard class which checks
if a coordinate has a particular colour.
# for check 3 we can perhaps make a method in the chessboard class
which checks if empty.
# for check 4 we can make a method in the rook class since it is rook
specific.
# for these we would need the rows and columns of the old and new
positions so let's first make a method for that in the chessboard class.
## 11
def is_valid_move(self,new_pos):
old_row = cb.get_row([Link])
old_col = cb.get_col([Link])
new_row = cb.get_row(new_pos)
new_col = cb.get_col(new_pos)
# new position shouldn't have same colour or be same as old position.
If it does, move is not valid. return False.
if cb.has_colour (new_pos,[Link]):
return False
# if both row and column are different, return False. Rook specific
condition.
if old_row != new_row and old_col != new_col:
return False
## 12a
# Let's create a method that finds all squares between two coords. And
then another method that checks if all such cells are empty. As this will be
useful for a bishop and a queen let's create this at the chessboard level.
## 14
if cb.path_blocked(old_row,old_col,new_row,new_col):
return False
# if there is none of the above problems, then True
return True
## 15
def move(self,new_pos):
if self.is_valid_move(new_pos):
# print piece captured if it is
if cb.get_pos(new_pos) not in [[Link], [Link]]:
print (cb.get_pos(new_pos), "captured")
## 16a We want to restore the original position back to empty. Let's
create a method for that
## 16c
cb.set_original([Link]) # restores original blank square
[Link] = new_pos
cb.set_pos([Link],[Link]) # updates piece's position
else:
print("invalid move")
## 7b
r1_w = rook('w',1,'a5')
[Link]()
# 12c checking
print(cb.get_middle_squares(1,1,4,4),cb.get_middle_squares(5,1,5,4),
cb.get_middle_squares(6,7,1,7))
# 13b checking
print(cb.path_blocked(0,0,7,0))
# 16d checking
r1_w.move('a1')
[Link]()
‘’’
## 17 Now let's try to make other pieces. This would be a good time to
look closely at the rook class and see how much of the code was rook
specific and how much is applicalble to all pieces. Let's redo our code,
putting the general code in the piece class, and the rook specific code in
the rook child class.
## 18a
class chessboard:
black = " X "
white = " O "
map = { "a" : 1 , "b" : 2, "c" : 3 , "d": 4, "e" : 5, "f" : 6, "g" : 7, "h" : 8}
board = []
for i in range (8):
[Link]([])
# appending a new empty row
for j in range (8):
if (i + j) % 2 == 0:
board[i].append(white)
# represents white
else:
board[i].append(black)
# represents black
def disp(self):
for row in [Link]:
print(row)
print('') # To give a gap
# e7 must be column 5, and row 7 from bottom but 2 from the top.
Indexing of the same for python must 5 -1 = 4, and 2 - 1 = 1. Thus e
becomes 4, and 7 becomes 1.
def get_pos(self, coord):
# fetches the current item or symbol stored in that position
try:
row = 8 - int(coord[1])
col = [Link] [coord[0]] - 1
return [Link][row][col]
except:
print("Position is not valid or not in the right format")
def set_pos(self, coord, value):
# Changes item or symbol in the given coordinate, to that contained in
'value'
try:
row = 8 - int(coord[1])
col = [Link] [coord[0]] - 1
[Link][row][col] = value
except:
print("Position is not valid or not in the right format")
def has_colour(self, coord, colour):
return self.get_pos(coord)[1] == colour
# get_pos(a5) may return Rw1
def get_row(self, coord):
row = 8 - int(coord[1])
return row
def get_col(self, coord):
col = [Link] [coord[0]] - 1
return col
def get_middle_squares(self,old_row,old_col,new_row,new_col):
# Note the shorthand if syntax below
r_step = 0 if old_row == new_row else 1 if old_row < new_row else -1
# if old_row > new_row
c_step = 0 if old_col == new_col else 1 if old_col < new_col else -1 #
if old_col > new_col
list = []
r = old_row + r_step
c = old_col + c_step
while not (r == new_row and c == new_col):
[Link]([r,c])
r += r_step
c += c_step
return list
def path_blocked(self,old_row,old_col,new_row,new_col):
list = self.get_middle_squares(old_row,old_col,new_row,new_col)
for x in list:
if [Link][x[0]][x[1]] not in [[Link],[Link]]:
return True
return False
def set_original(self,coord):
i = self.get_row(coord)
j = self.get_col(coord)
# white is at [0,0] (top left corner) Black is at [0,1] [1, 0] etc.
if (i + j) % 2 == 0:
[Link][i][j] = [Link]
# represents white
else:
[Link][i][j] = [Link]
# represents black
cb = chessboard()
## 18b
class piece:
# shall be overwritten in the child class
symbol = '?'
# not specific to rook
def __init__ (self,colour,piece_no,start_pos):
[Link] = colour
[Link] = piece_no
[Link] = start_pos
[Link] += colour
[Link] += str(piece_no)
# appending the colour and piece number to the main symbol
cb.set_pos([Link],[Link])
# changes item at [Link] aka start_pos to [Link]
# Not rook specific
def move(self,new_pos):
if self.is_valid_move(new_pos):
# print piece captured if it is
if cb.get_pos(new_pos) not in [[Link], [Link]]:
print (cb.get_pos(new_pos), "captured")
cb.set_original([Link]) # restores original blank square
[Link] = new_pos
cb.set_pos([Link],[Link]) # updates piece's position
else:
print("invalid move")
## 18c
class rook(piece):
symbol = 'R'
# Overwrites '?' of the parent class
def is_valid_move(self,new_pos):
old_row = cb.get_row([Link])
old_col = cb.get_col([Link])
new_row = cb.get_row(new_pos)
new_col = cb.get_col(new_pos)
# new position shouldn't have same colour or be same as old position.
If it does, move is not valid. return False.
if cb.has_colour (new_pos,[Link]):
return False
# if both row and column are different, return False. Rook specific
condition.
if old_row != new_row and old_col != new_col:
return False
if cb.path_blocked(old_row,old_col,new_row,new_col):
return False
# if there is none of the above problems, then True
return True
## 19 Now let's make a bishop
class bishop(piece):
symbol = 'B'
# Overwrites '?' of the parent class
def is_valid_move(self,new_pos):
old_row = cb.get_row([Link])
old_col = cb.get_col([Link])
new_row = cb.get_row(new_pos)
new_col = cb.get_col(new_pos)
# new position shouldn't have same colour or be same as old position.
If it does, move is not valid. return False.
if cb.has_colour (new_pos,[Link]):
return False
# if on same diagonal then differece between rows is equal to
difference between columns. If NOT then return False(invalid move). This
is Bishop specific
if abs(old_row - new_row) != abs( old_col - new_col):
return False
# path should not be blocked was not rook specific hence no change
if cb.path_blocked(old_row,old_col,new_row,new_col):
return False
# if there is none of the above problems, then True
return True
## 20 Now let's make a queen
class queen (piece):
symbol = 'Q'
# Overwrites '?' of the parent class
def is_valid_move(self,new_pos):
old_row = cb.get_row([Link])
old_col = cb.get_col([Link])
new_row = cb.get_row(new_pos)
new_col = cb.get_col(new_pos)
# new position shouldn't have same colour or be same as old position.
If it does, move is not valid. return False.
if cb.has_colour (new_pos,[Link]):
return False
# if on same diagonal then differece between rows is equal to
difference between columns. If NOT then return False(invalid move). This
is Bishop specific.
if abs(old_row - new_row) != abs( old_col - new_col):
# if both row and column are different, return False. Rook specific
condition.
# Nested if because if it can neither move like a bishop nor like a
rook it can not move like a queen.
if old_row != new_row and old_col != new_col:
return False
# path should not be blocked was not rook specific hence no change
if cb.path_blocked(old_row,old_col,new_row,new_col):
return False
# if there is none of the above problems, then True
return True
'''
## 18 d
r1_w = rook('w',1,'a5')
[Link]()
r1_w.move('a1')
[Link]()
'''
'''
## 19
r1_w = rook('w',1,'a1')
r1_b = rook('b',1, 'a5')
b1_w = bishop('w',1,'b1')
b1_b = bishop ('b', 1,'g6')
[Link]()
r1_w.move('a5')
[Link]()
b1_b.move('b1')
[Link]()
'''
## 20
r1_w = rook('w',1,'a1')
r1_b = rook('b',1, 'a5')
b1_w = bishop('w',1,'b1')
b1_b = bishop ('b', 1,'g6')
q1_w = queen('w',1,'d1')
q1_b = queen('b',1,'d8')
r1_w.move('a5')
[Link]()
b1_b.move('b1')
[Link]()
q1_w.move('b1')
q1_b.move('a5')
[Link]()
# if not ((abs(old_row - new_row) == 2 and abs(old_col - new_col) == 1)
or (abs(old_row - new_row) == 1 and abs(old_col - new_col) == 2)):
# return False
# Knight