I wasn't quite sure how to word the title, sorry if it doesn't make sense/is misleading.
Note - a "boat" is 3 O's next to each other in an array, so
_|O|_ _|_|_
_|O|_ O|O|O
|O| | |
are boats.
So I have an a list of lists (n x n) (working with lists) in which I generate n boats at random spaces. I don't want the boats to be next to each other, touch by corners, or be on top of each other.
A tried checking if a boat would end up on top of another boat in the vein of this:
if board[y - 2][x] == 'O' or board[y + 2][x] == 'O' ...
and so on, which ended up being unsurprisingly long.
I also got index out of range errors, since I was sometimes checking for coordinates not in the field.
So, is there a way I can check for boats in every direction without going out of index range?
Better yet, any ideas on how to make the boats not generate next to each other?
The code for boat generation is here:
from random import *
side = int(input())
game_state = []
def generate_initial_state():
for i in range(side):
game_state.append([])
for j in range(side):
game_state[i].append('.')
for i in range(side):
# Generate boat origin on random coordinates within the game board,
# if there's a boat already, generate new ones
y_cor = randint(0, side-1)
x_cor = randint(0, side-1)
while game_state[y_cor][x_cor] == 'O':
y_cor = randint(0, side - 1)
x_cor = randint(0, side - 1)
# Direct chooses if the boat will be generated up, down, or sideways
direct = randint(1, 4)
cycle = 0
while cycle < 3:
# Generates a boat going from origin in one direction,
# if the boat would end outside the board, chooses a different direction
if direct == 1:
if y_cor + 2 >= side:
direct = randint(1, 4)
else:
game_state[y_cor + cycle][x_cor] = 'O'
cycle += 1
elif direct == 2:
if x_cor + 2 >= side:
direct = randint(1, 4)
else:
game_state[y_cor][x_cor + cycle] = 'O'
cycle += 1
elif direct == 3:
if y_cor - 2 < 0:
direct = randint(1, 4)
else:
game_state[y_cor - cycle][x_cor] = 'O'
cycle += 1
elif direct == 4:
if x_cor - 2 < 0:
direct = randint(1, 4)
else:
game_state[y_cor][x_cor - cycle] = 'O'
cycle += 1
for i in range(side):
print(*game_state[i])
First I would only use two directions (horizontal and vertical), which shouldn't change the probabilities (with your model, a boat can be generated in two ways).
This allows index overflow to only occur by exceeding the allowable indices, which provokes an IndexError that can be intercepted (using a negative index doesn't and that could mess up your generator).
Secondly, using a flag could help you do the trick.
I added a few other modifications :
EDIT : I just realized that my code was rejecting perfectly valid boats if they were on the border, so here is a version that doesn't.
Update : some explanations
We use a boolean flag boat_built to track the suitability of a randomly chosen position of the boat: once all tests are made, this variable decides if the choice was suitable (True) or if obstructions were encountered while tests were carried out (False).
Using
boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")
we update the flag for every test : if boat_built was False before the test, it will remain False regardless of the test's result (False & a = False): this is intended, because it means that an obstruction has already been encountered and the boat is invalid.
On the other hand, if boat_built was True before the test, it will contain the result of the test afterwards (True & a = a): this is also intended, since failing the new test means we now have found an obstruction.
Note that all 15 tests are carried out for every boat, even if an obstruction is encountered early on.
from random import *
side = int(input())
game_state = [['.' for i in range(side)] for j in range(side)]
l_dir = [(1, 0), (0, 1)]
def generate_initial_state():
for i in range(side):
boat_built = False
while not boat_built:
boat_built = True
y_cor = randrange(side)
x_cor = randrange(side)
dx, dy = l_dir[randrange(2)]
try:
# check that the three required cells are empty
for k in range(3):
boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")
except IndexError:
# if any is out of range, choice is invalid
boat_built = False
for k in range(5):
for l in [-1, 1]:
try:
# check if neighbours on the long sides are empty
boat_built &= (game_state[x_cor + (k-1)*dx + l*dy][y_cor + l*dx + (k-1)*dy] != "0")
except IndexError:
# if we're out of range, no obstruction
pass
for k in [-1, 3]:
try:
# check if neighbours on the short sides are empty
boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")
except IndexError:
# again, if we're out of range, no obstruction
pass
# if we reach this point, a valid position has been found
for k in range(3):
game_state[x_cor + k*dx][y_cor + k*dy] = "0"
generate_initial_state()
for i in range(side):
print(*game_state[i])
Your code has, as you complain, too much tedious repeated logic to inspect a point's neighbors. You could turn four tests into one with something like this:
offset = [
(0, -1),
(-1, 0), (1, 0),
(0, 1),
]
for xoff, yoff in offset:
if game_state[x + xoff * cycle][y + yoff * cycle] == 'O':
report_collision(x, y)
Additionally, you could mark the grid with both 'O' for "boat" and 'o' for "bordering a boat", to simplify detecting adjacent boats.
You can try the following class and see if it solves your problem:
#! /usr/bin/env python3
import collections
import enum
import random
def main():
board = Board(10, 10)
print(board)
board.place_boats([2, 3, 3, 4, 5])
print('\n' + '=' * 21 + '\n')
print(board)
Point = collections.namedtuple('Point', 'x, y')
# noinspection PyArgumentList
Orientation = enum.Enum('Orientation', 'HORIZONTAL, VERTICAL')
class Board:
def __init__(self, width, height):
self.__width = width
self.__height = height
self.__matrix = [[False] * height for _ in range(width)]
self.__available = {Point(x, y)
for x in range(width)
for y in range(height)}
def __str__(self):
width = self.__width * 2 + 1
height = self.__height * 2 + 1
grid = [[' '] * width for _ in range(height)]
for yo, xo, character in (0, 1, '|'), (1, 0, '-'), (1, 1, '+'):
for y in range(yo, height, 2):
for x in range(xo, width, 2):
grid[y][x] = character
for x, column in enumerate(self.__matrix):
for y, cell in enumerate(column):
if cell:
grid[y << 1][x << 1] = '#'
return '\n'.join(''.join(row) for row in grid)
# noinspection PyAssignmentToLoopOrWithParameter
def place_boats(self, sizes, patience=10):
matrix_backup = [column.copy() for column in self.__matrix]
available_backup = self.__available.copy()
for _ in range(patience):
# try to place all the boats
for size in sizes:
for _ in range(patience):
# try to place boat of current size
point = random.choice(tuple(self.__available))
method = random.choice(tuple(Orientation))
try:
# try to place a boat; does not mangle the matrix
self.make_boat(point, size, method)
except RuntimeError:
pass
else:
# break out of inner patience loop; go to next size
break # on success
else:
# break to outer patience loop; start from beginning
self.__matrix = [column.copy() for column in matrix_backup]
self.__available = available_backup.copy()
break # on failure
else:
# break out of outer patience loop; all sizes were placed
break # on success
else:
raise RuntimeError('could not place the requested boats')
def make_boat(self, point, size, method):
backup = [column.copy() for column in self.__matrix]
unusable = set()
for offset in range(size):
if method is Orientation.HORIZONTAL:
block = self.mark_cell(point, x_offset=offset)
elif method is Orientation.VERTICAL:
block = self.mark_cell(point, y_offset=offset)
else:
raise ValueError('method was not understood')
if block:
unusable.update(block)
else:
self.__matrix = backup
raise RuntimeError('cannot place boat')
self.__available -= unusable
def mark_cell(self, point, *, x_offset=0, y_offset=0):
target = Point(point.x + x_offset, point.y + y_offset)
if target in self.__available and \
0 <= target.x < self.__width and \
0 <= target.y < self.__height:
self.__matrix[target.x][target.y] = True
return {Point(target.x + xo, target.y + yo)
for xo in range(-1, 2)
for yo in range(-1, 2)}
if __name__ == '__main__':
main()
Related
I'm trying to create Tetris for an university project. But I'm having a quite difficult time on doing the collisions. Actually, they work, until I try to rotate a piece. After I rotate a piece the collision always returns True.
This is the code:
def constraint(self):
if self.shape_y + len(self.shape) == configuration.config['rows']:
return True
for shape_row, row in enumerate(self.shape):
column_index = -1
for x in range(self.shape_x, self.shape_x + len(self.shape[0])):
column_index += 1
if self.shape[shape_row][column_index] != 0:
if shape_row+1 < len(self.shape):
if self.shape[shape_row+1][column_index] == 0:
if self.board.board[self.shape_y + 1][x] != 0:
return True
else:
if self.board.board[self.shape_y + len(self.shape)][x] != 0:
print("qui")
return True
return False
shape_y is the row where the shape is located. len(self.shape) returns how many rows is the shape because it's coded like a matrix:
Example:
[[0, 1, 0],
[1, 1, 1]],
Is the piece with one block on top and three under.
Shape is the matrix that represent the piece.
Shape_x is the column where the shape is located.
The board is a matrix like this:
self.board = np.array([[0 for _ in range(configuration.config["cols"])]
for _ in range(configuration.config['rows'])])
where 0 is free, others numbers are block that are not free.
Here a screenshot that show the problem:
The blue and the green pieces get stuck like a collision occurred but are in "mid air" and nothing really occurred.
EDIT1:
This is the code for the rotation
def rotate(self):
self.board.remove_piece(self)
self.shape = np.rot90(self.shape)
self.board.add_piece(self)
Where self.board.remove_piece(self) and self.board.add_piece(self) just remove and add the values inside the board so that i can draw it again. So, basically, the rotation code is just self.shape = np.rot90(self.shape)
I should have actually fixed the problem, the error was inside an index to check on the board.
def constraint(self):
if self.shape_y + len(self.shape) == configuration.config['rows']:
return True
for shape_row, row in enumerate(self.shape):
column_index = -1
for x in range(self.shape_x, self.shape_x + len(self.shape[0])):
column_index += 1
if self.shape[shape_row][column_index] != 0:
if shape_row+1 < len(self.shape):
if self.shape[shape_row+1][column_index] == 0:
if self.board.board[self.shape_y + shape_row + 1][x] != 0:
return True
else:
if self.board.board[self.shape_y + len(self.shape)][x] != 0:
return True
return False
Currently, I can create a randomized world within a 2-D array. However, I feel it is too random. Here is the class I'm currently working with:
from random import randint, choice, randrange
class WorldSpace(object):
def __init__(self, row, col, world_array):
self.row = row # Number of lists to be created.
self.col = col # Number of indexes inside of each row.
self.world_array = world_array
The WorldSpace method that creates the world:
#classmethod
def generate(cls, autogen):
print 'Starting world generation...'
print
if autogen is True:
row_amt = 75
col_amt = 75
else:
row_amt = input('Please specify row length of world array.: ')
col_amt = input('Please specify column length of world array.: ')
if (row_amt or col_amt) == 0:
print 'Invalid world values!'
cls.generateWorld(False)
world_array = [[' ']*col_amt for _ in xrange(row_amt)]
print 'Created world...'
return cls(row_amt, col_amt, world_array)
A method that modifies the world -- currently only creates forests though in my full segment of code, a series of oceans and mountains are formed as well:
def modify_world(self, autogen):
if autogen is True:
# Forests:
count = randint(6, 10)
while count > 0:
a = randint(1, (self.row / randint(2, 6)))
b = randint(1, (self.col / randint(2, 6)))
row_val = randint(5, self.row)
count_val = randint(5, 15)
self.genLine_WObj(a, b, row_val, 't', count_val)
count -=1
print('\n'.join([''.join(['{:1}'.format(item) for item in row])
for row in self.world_array]))
inp = input('')
if inp != '':
return
And the method that actually creates the forest tiles:
def genLine_WObj(self, a, b, row_val, char, count):
# 'genLine_WObj' - Generate Line(like) world object.
# Used to place lines of certain characters with psuedo-randomized
# width and length onto the world array.
while count != 0:
row_col_dict = {row_val: (a, b)}
for row in row_col_dict.keys():
startPos, endPos = row_col_dict[row]
for i in range(startPos, endPos):
self.world_array[row][i] = char
b += choice([0, 1])
a += choice([0, 0, 0, 0, 1])
row_val -= 1
count -= 1
Now to actually run the program:
world = WorldSpace.generate(True)
world.modify_world(True)
However, while the works ~20-30% of the time, sometimes it will generate small forests, or small pairs of t characters, when it should be creating forests all around the map. How can I improve my code to make the randomized generation more consistent?
Fixed:
Sometimes your a is bigger than your b and forest is not generated.
All your forests tend to be at the left side of map.
Inline comments are added for changed lines.
def modify_world(self, autogen):
if autogen is True:
# Forests:
count = randint(6, 10)
while count > 0:
a = randrange(self.col) # begin of forest
width = self.col / randint(2, 6) # initial width of forest
b = int(a + width) # end of forest
row_val = randint(5, self.row)
count_val = randint(5, 15)
self.genLine_WObj(a, b, row_val, 't', count_val)
count -=1
print('\n'.join([''.join(['{:1}'.format(item) for item in row])
for row in self.world_array]))
inp = input('')
if inp != '':
return
def genLine_WObj(self, a, b, row_val, char, count):
# 'genLine_WObj' - Generate Line(like) world object.
# Used to place lines of certain characters with psuedo-randomized
# width and length onto the world array.
while count != 0:
row_col_dict = {row_val: (a, b)}
for row in row_col_dict.keys():
startPos, endPos = row_col_dict[row]
for i in range(startPos, min(self.col, endPos)): # added min
self.world_array[row][i] = char
b += choice([0, 1])
a += choice([0, 0, 0, 0, 1])
row_val -= 1
count -= 1
And I would change width of forest to something mapsize-independent. E. g.:
width = randint(2, 15)
But that depends on your goals.
There is a robot at the top-left corner of an N*M grid. The robot can move up, down, left and right, but cannot visit the same cell more than once in each traversal. How do I find the total number of ways the robot can reach the bottom-right corner?
(the robot does not need to visit every cell for a path to be valid)
I think there is a recursive solution to this but I can't get it somehow.
Here's what I've got so far:
def initialize(row, cols):
grid = [ [ 0 for c in range(cols) ] for r in range(rows) ]
pos_r, pos_c = 0, 0
grid[pos_r][pos_c] = 1 # set start cell as visited
dst_x, dst_y = len(grid)-1, len(grid[0])-1 # coords of bottom-right corner
print move_robot(grid, dst_x, dst_y, pos_r, pos_c)
def move_robot(grid, dst_x, dst_y, pos_r, pos_c, prev_r=None, prev_c=None):
num_ways = 0
if reached_dst(dst_x, dst_y, pos_r, pos_c):
undo_move(grid, pos_r, pos_c)
undo_move(grid, prev_r, prev_c)
return 1
else:
moves = get_moves(grid, pos_r, pos_c)
if len(moves) == 0:
undo_move(grid, prev_r, prev_c)
return 0
for move in moves:
prev_r = pos_r
prev_c = pos_c
pos_r = move[0]
pos_c = move[1]
update_grid(grid, pos_r, pos_c)
num_ways += move_robot(grid, dst_x, dst_y, pos_r, pos_c, prev_r, prev_c)
return num_ways
if __name__ == '__main__':
initialize(4, 4)
I left out some function definitions for brevity. Get_moves retrieves the all legal moves, checking whether each move would still be on the board and whether the cell has already been visited. Update_grid sets the specified cell to '1', which means visited. Undo_move does the opposite, setting the specified cell to '0'.
I get the right answer for the simplest possible case (2*2 grid), but for larger grids the output is always too low. What's wrong with my code, and is there a simpler way of doing this?
The recursion is pretty straight forward but one should be careful to create copies of the matrix while recursing in order to receive good results:
from copy import copy, deepcopy
def calc(i, j, mat):
if i < 0 or j < 0 or i >= len(mat) or j >= len(mat[0]):
return 0 # out of borders
elif mat[i][j] == 1:
return 0 # this cell has already been visited
elif i == len(mat)-1 and j == len(mat[0])-1:
return 1 # reached destination (last cell)
else:
mat[i][j] = 1 # mark as visited
# create copies of the matrix for the recursion calls
m1 = deepcopy(mat)
m2 = deepcopy(mat)
m3 = deepcopy(mat)
m4 = deepcopy(mat)
# return the sum of results of the calls to the cells:
# down + up + right + left
return calc(i+1, j, m1) + calc(i-1, j, m2) + calc(i, j+1, m3) + calc(i, j-1, m4)
def do_the_robot_thing(m, n):
# an un-visited cell will be marked with "0"
mat = [[0]*n for x in xrange(m)]
return calc(0, 0, mat)
print(do_the_robot_thing(3, 3))
OUTPUT:
12
So far, I have a program where 2 players can click to place an X and an O in turns. I'm not sure how to make the program recognize a winner/ draw. If you guys could help me make a function that indicated a win/ draw on the screen in any way, I would love you forever. Thanks.
from graphics import *
import sys
def player_o(win, center):
'''
Parameters:
- win: the window
'''
outline_width = 5
circle = Circle(center, boxsize/2)
circle.setOutline('red')
circle.setWidth(outline_width)
circle.draw(win)
def player_x(win, p1x, p1y):
'''
Parameters:
- win: the window
'''
for i in range(2):
deltaX = (-1) ** i * (boxsize / 2)
deltaY = (boxsize / 2)
line = Line(Point(p1x - deltaX, p1y - deltaY),
Point(p1x + deltaX, p1y + deltaY))
line.setFill('red')
line.setWidth(5)
line.draw(win)
def game():
global win
global boxsize
try:
winsize = int(input("How large would you like the window? (Between 100 and 3000): "))
if winsize < 100 or winsize > 3000:
print("Invalid window size")
quit()
squares = int(input("How many squares per row? (Between 3 and 10):"))
boxsize = winsize/ squares
if squares < 3 or squares > winsize / 10:
print("Invalid number")
quit()
except ValueError:
sys.exit("Not a valid number")
win = GraphWin("Tic Tac Toe", winsize, winsize)
for i in range(squares - 1):
hline = Line(Point(0, (winsize/squares) * (i + 1)), Point(winsize, (winsize/squares) * (i + 1)))
hline.draw(win)
vline = Line(Point((winsize/squares) * (i + 1), 0), Point((winsize/squares) * (i + 1), winsize))
vline.draw(win)
for i in range((squares ** 2) // 2):
print("X, click a square.")
p1mouse = win.getMouse()
p1x = p1mouse.getX()
p1y = p1mouse.getY()
player_x(win, p1x, p1y)
print("O, click a square.")
p2mouse = win.getMouse()
p2x = p2mouse.getX()
p2y = p2mouse.getY()
player_o(win, Point(p2x, p2y))
if squares % 2 == 1:
print("X, click a square.")
p1mouse = win.getMouse()
p1x = p1mouse.getX()
ply = p1mouse.getY()
player_x(win, p1x, p1y)
game()
Keep data and representation of data separated. That's how. Right now you're just drawing things, rather than that you should be generating some representation of the playing field (e.g. a list of the boxes and their state, as in, checked by p1, checked by p2, or unchecked), and then use that to draw when needed. The advantage should be immediately obvious - if you know the state of the game, determining if there's a winner (and who it is) is trivial.
After 3 turns (minimum turns to win) check your 2d array if there is a token next to the last played by adding/substracting one, if found repeat same operation to array indices else break out.
If 2nd control structure is reached break and announce winner.
With each move in the game, a 2D array or a dictionary (with values being lists) should be used. Then, you can just check each way of winning. This way, you can also check if the move is valid or not--- whether or not the spot on the board is taken.
I would also suggest using a numerical or a coordinate system to dictate movement.
The board would look like this:
1 2 3
4 5 6
7 8 9
The numbers are corresponding spots on the board.
For example:
In the initialization:
moves = 0
positions = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0, '9':0}
# '1' - '9' are the spots on the board.
# 0 means the spot is empty, 'X' means the spot is taken by 'X', 'O' means the spot is taken by 'O'. You can use any other naming system, but this is simple.
In the movement code:
while 1 == 1: # just a loop until the input is valid. See the 'break' statement below
new_move = input("X, enter what space to move to: ")
if positions[new_move] == 0: # if that board spot is empty
moves += 1 #moves = moves + 1
positions[new_move] == 'X' # board spot is now occupied by 'X'
# code to show the piece on the board
if moves >= 5: # least possible moves to win is 5
win_check(positions)
break
Alternatively, you can use the movement as a function, and have it recursively call itself until the input is valid:
def move_X():
new_move = input("X, enter what space to move to: ")
if positions[new_move] == 0: # if that board spot is empty
moves += 1 #moves = moves + 1
positions[new_move] == 'X' # board spot is now occupied by 'X'
# code to show the piece on the board
if moves >= 5: # least possible moves to win is 5
win_check(positions)
move_O() # this should be defined similarly to 'move_X' except that it would correlate to 'O'.
else:
move_X()
The the win checking method:
def win_check(positions):
if positions['1'] == 'X' and positions['2'] == 'X' and positions['3'] == 'X':
return "Winner: X"
elif # similar things, checking all of the other ways to win.
You need 1 if statement (in the beginning) and 15 elif statements, as there are 8 ways to win for each player, so 16 checks have to be made.
(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.