6
\$\begingroup\$

This is a problem from CodeSignal which can be founded over here

Here is an image on what we have to do:

enter image description here

Code

def map(cell):
    new_cell1 = ''
    
    for i in cell:
        if i == 'a':
            new_cell1 += '1'
        if i == 'b':
            new_cell1 += '2'
        if i == 'c':
            new_cell1 += '3'
        if i == 'd':
            new_cell1 += '4'
        if i == 'e':
            new_cell1 += '5'
        if i == 'f':
            new_cell1 += '6'
        if i == 'g':
            new_cell1 += '7'
        if i == 'h':
            new_cell1 += '8'
            
    new_cell1 += cell[-1]
    return new_cell1
    
def chessKnight(cell):
    cell = map(cell)
    num_of_moves = 0
    
    if (int(cell[0])+1 <= 8 and int(cell[0])+1 > 0) and (int(cell[1])+2 <= 8 and int(cell[1])+2 > 0):
        num_of_moves += 1
        
    if (int(cell[0])-1 <= 8 and int(cell[0])-1 > 0) and (int(cell[1])+2 <= 8 and int(cell[1])+2 > 0):
        num_of_moves += 1
        
    if (int(cell[0])+1 <= 8 and int(cell[0])+1 > 0) and (int(cell[1])-2 <= 8 and int(cell[1])-2 > 0):
        num_of_moves += 1
        
    if (int(cell[0])-1 <= 8 and int(cell[0])-1 > 0) and (int(cell[1])-2 <= 8 and int(cell[1])-2 > 0):
        num_of_moves += 1
        
    if (int(cell[0])+2 <= 8 and int(cell[0])+2 > 0) and (int(cell[1])+1 <= 8 and int(cell[1])+1 > 0):
        num_of_moves += 1
    
    if (int(cell[0])-2 <= 8 and int(cell[0])-2 > 0) and (int(cell[1])+1 <= 8 and int(cell[1])+1 > 0):
        num_of_moves += 1
    
    if (int(cell[0])+2 <= 8 and int(cell[0])+2 > 0) and (int(cell[1])-1 <= 8 and int(cell[1])-1 > 0):
        num_of_moves += 1
    
    if (int(cell[0])-2 <= 8 and int(cell[0])-2 > 0) and (int(cell[1])-1 <= 8 and int(cell[1])-1 > 0):
        num_of_moves += 1
        
    return num_of_moves

Question

The Code works as expected and returns the right answer, but I have just put a bunch of if conditions which doesn't look nice to me. Is there any way to implement the problem but without so much if blocks?

\$\endgroup\$
1
  • \$\begingroup\$ Please note that when you link a question from code signal, the question is locked for all of those who haven't solved the first problems, due to which the higher-level questions are locked \$\endgroup\$ – Parekh Oct 25 '20 at 19:10
4
\$\begingroup\$

Never use existing function names for new functions

Python already has a function called map()
Defining your new map() function can cause a lot of confusion, and undefined behaviour in your program.

By cheating

Since you have asked for an alternate solution, here it is

Being a chess engine developer, I would never calculate something trivial like the number of knight attacks since a simple array of size 64 with the pre-calculated ones can easily work. All you need is a simple function that converts a square like a1 to 0 and h8 to 63.

Here is the implementation,

def str_sq_to_int(sq):
    return (ord(sq[0])-97) + ((ord(sq[1])-49) * 8);

def knightAttacks(cell):
    attacks = [
        2, 3, 4, 4, 4, 4, 3, 2,
        3, 4, 6, 6, 6, 6, 4, 3,
        4, 6, 8, 8, 8, 8, 6, 4,
        4, 6, 8, 8, 8, 8, 6, 4,
        4, 6, 8, 8, 8, 8, 6, 4,
        4, 6, 8, 8, 8, 8, 6, 4,
        3, 4, 6, 6, 6, 6, 4, 3,
        2, 3, 4, 4, 4, 4, 3, 2
    ]
    return attacks[str_sq_int(cell)]

The explanation is simple, the function str_sq_to_int() takes a square like 'a1' and returns an index using the ASCII value of the character to calculate the index. You can also use a simple dictionary to map each square to an index, but this one is easy

Then, it uses a pre-calculated set of values, to return the correct answer.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Lol. This answer is very cool, \$\endgroup\$ – fartgeek Oct 25 '20 at 20:32
  • 1
    \$\begingroup\$ @fartgeek yeah 😁, I kind of copy pasted the str_to_int function from my chess engine though xD. I would recommend bit manipulation but since it's python, doesn't make sense \$\endgroup\$ – Parekh Oct 25 '20 at 20:49
  • \$\begingroup\$ To improve this answer, I would suggest making str_sq_to_int, ord and sq more explicit and making the casing consistent. Also the magic numbers (97, 49, 8) could use some names. \$\endgroup\$ – Raimund Krämer Oct 26 '20 at 9:20
  • \$\begingroup\$ @RaimundKrämer Is that why you downvoted? It is good advice, I will remove the magic numbers but I'm not sure what name I could get for them since they are based on the ascii values \$\endgroup\$ – Parekh Oct 26 '20 at 11:12
  • \$\begingroup\$ @RaimundKrämer I'm a little unsure as to what you expect more in this answer, may i suggest you edit it yourself? I would like to see what I missed that deserved a downvote \$\endgroup\$ – Parekh Oct 26 '20 at 11:18
1
\$\begingroup\$

Alright. This is an interesting question. If you think about it, the moves of a knight have a few conditions (excluding checks and whether a friendly piece is on the destination square):

  • The move has to be on the board (this is one is obvious)

  • The absolute value of the distance moved to the side subtracted from the absolute value of the distanced moved up must either equal one or -1.

  • The absolute value of the distance moved to the side must be one or two.

  • The absolute value of the distance moved up or down must be either one or two.

The code is as follows :

def knight_move_counter(position_on_board):
    possibleMoves = 0
    # here we could use ASCII values to convert the letters to numbers, but a dict is easier to visualize
    letterToNumbers = {
        "a" : 0,
        "b" : 1,
        "c" : 2,
        "d" : 3,
        "e" : 4,
        "f" : 5,
        "g" : 6,
        "h" : 7
    }
    start_letter = letterToNumbers[position_on_board[0]]
    start_number = int(position_on_board[1]) - 1 # note that this line and the preceding one will only work if they are valid algebraic notation.
    for iterator in range(8):
        for second_iterator in range(8): # these two for loops assure our answers will be on the board.
            if abs( abs(start_letter - iterator) - abs(start_number - second_iterator)) == 1: # point #2
                if abs(start_letter - iterator) == 1 or abs(start_letter - iterator) == 2: # point #3
                    if abs(start_number - second_iterator) == 1 or abs(start_number - second_iterator) == 2: # point #4
                        possibleMoves += 1
    return possibleMoves


print(knight_move_counter("a1")) # you can replace the "a1" with whatever you would like, as long as it is valid algebraic notation.
\$\endgroup\$
1
  • \$\begingroup\$ this article on knight patterns is really cool \$\endgroup\$ – Parekh Oct 26 '20 at 5:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.