Related
I'm learning unit testing and TDD in python and have got the basics mastered with pytest.
I've started a learning project of writing a sudoku solver and one thing I'm realising is that at the start I'm not currently sure how I want to store the data for the sudoku board. Many talks warn about testing behaviour over implementation. Would the board data structure count as implementation?
It's very hard to write a test that is independent of the board data structure.
For example you could store the values of each cell as an integer (eg 1) or a string (eg '1') and the board as a whole can be a dict (using cell label like 'A1' or 'C5' etc as keys), list (flat or 2d) or a 81 character string.
So I might start with
hardboard = [
[0, 0, 0, 0, 0, 0, 4, 0, 0],
[0, 0, 1, 0, 7, 0, 0, 9, 0],
[5, 0, 0, 0, 3, 0, 0, 0, 6],
[0, 8, 0, 2, 0, 0, 0, 0, 0],
[7, 0, 0, 0, 0, 0, 9, 2, 0],
[1, 2, 0, 6, 0, 5, 0, 0, 0],
[0, 5, 0, 0, 0, 0, 0, 4, 0],
[0, 7, 3, 9, 0, 8, 0, 0, 0],
[6, 0, 0, 4, 5, 0, 2, 0, 0]
]
but then decide to use a dict
hardboard = {'A1': 0, 'A2': 0, 'A3': 0, ... 'I9': 0}
So how should I start? The only solution I can think of is to commit to one format, at least in terms how I represent the board in tests, even if the actual code uses a different data structure later and has to translate. But in my TDD noobiness I'm unsure if this is a good idea?
I'm not a python developer, but still:
Don't think about the board in terms of implementation details (like, this is a dictionary, array, slice, whatever).
Instead imagine that the board is an abstraction and it has some behaviors - which are in plain words - things you can do with a board:
place a digit in an index(i,j) that should be in 1 to 9 inclusive boundaries
remove a digit by index (i,j)
So its like you have a "contract" - you create a board and can execute some operations on it. Of course it has some internal implementation, but its hidden from everyone who uses it. Its your responsibility to provide a working implementation but this is what you want to test: The implementation is considered working if it "reacts" on all the operations correctly (behavior = operation = function at the level of actual programming implementation)
In this case your test (schematically might look like this):
Board Tests:
Test 1 : Check that its impossible to put a negative i index:
// setup
board = ...
// when:
board.put(-1, 3, 8) // i-index is wrong
// then:
expect exception to be thrown
Test 2 : Check that its impossible to put a negative j index:
// setup
board = ...
// when:
board.put(0, -1, 5) // j-index is wrong
// then:
expect exception to be thrown
Test 3 : Check that its impossible to put a negative number into cell:
// setup
board = ...
// when:
board.put(0, 0, -7)
// then:
expect exception to be thrown
Test 4 : Check that its impossible to put number that alreay contains another number:
// setup
board = ...
// when:
board.put(0, 0, 6)
board.put(0, 0, 5)
// then:
expect exception to be thrown
... and so on and so forth (also cover with tests for getting a cell, and any other additional behavior you create for the board).
Note, that all these tests indeed check the behavior of your abstraction rather than internal implementation.
At the level of implementation you can create a class for the board, internal data fields will contain the state, whereas the behavior will be represented by the methods exposed to the users of the class.
So I'm doing the foo.bar challenge, and I've got code in python that outputs the required answers. I know for a fact that for at least the first two test cases my output matches their output but it still fails all of them. I assumed it could be because its running in python 2.7.13 so I found an online sandbox that runs that version of python but my code still outputs the required output there too. I've tried using the print function to output the results, I've tried formatting the results as lists and arrays but none of this worked. The question is below:
Doomsday Fuel
Making fuel for the LAMBCHOP's reactor core is a tricky process
because of the exotic matter involved. It starts as raw ore, then
during processing, begins randomly changing between forms, eventually
reaching a stable form. There may be multiple stable forms that a
sample could ultimately reach, not all of which are useful as fuel.
Commander Lambda has tasked you to help the scientists increase fuel
creation efficiency by predicting the end state of a given ore sample.
You have carefully studied the different structures that the ore can
take and which transitions it undergoes. It appears that, while
random, the probability of each structure transforming is fixed. That
is, each time the ore is in 1 state, it has the same probabilities of
entering the next state (which might be the same state). You have
recorded the observed transitions in a matrix. The others in the lab
have hypothesized more exotic forms that the ore can become, but you
haven't seen all of them.
Write a function solution(m) that takes an array of array of
nonnegative ints representing how many times that state has gone to
the next state and return an array of ints for each terminal state
giving the exact probabilities of each terminal state, represented as
the numerator for each state, then the denominator for all of them at
the end and in simplest form. The matrix is at most 10 by 10. It is
guaranteed that no matter which state the ore is in, there is a path
from that state to a terminal state. That is, the processing will
always eventually end in a stable state. The ore starts in state 0.
The denominator will fit within a signed 32-bit integer during the
calculation, as long as the fraction is simplified regularly.
For example, consider the matrix m: [ [0,1,0,0,0,1], # s0, the
initial state, goes to s1 and s5 with equal probability
[4,0,0,3,2,0], # s1 can become s0, s3, or s4, but with different
probabilities [0,0,0,0,0,0], # s2 is terminal, and unreachable
(never observed in practice) [0,0,0,0,0,0], # s3 is terminal
[0,0,0,0,0,0], # s4 is terminal [0,0,0,0,0,0], # s5 is terminal ]
So, we can consider different paths to terminal states, such as: s0 ->
s1 -> s3 s0 -> s1 -> s0 -> s1 -> s0 -> s1 -> s4 s0 -> s1 -> s0 -> s5
Tracing the probabilities of each, we find that s2 has probability 0
s3 has probability 3/14 s4 has probability 1/7 s5 has probability 9/14
So, putting that together, and making a common denominator, gives an
answer in the form of [s2.numerator, s3.numerator, s4.numerator,
s5.numerator, denominator] which is [0, 3, 2, 9, 14].
Languages
To provide a Java solution, edit Solution.java To provide a Python
solution, edit solution.py
Test cases
========== Your code should pass the following test cases. Note that it may also be run against hidden test cases not shown here.
-- Java cases -- Input: Solution.solution({{0, 2, 1, 0, 0}, {0, 0, 0, 3, 4}, {0, 0, 0, 0, 0}, {0, 0, 0, 0,0}, {0, 0, 0, 0, 0}}) Output:
[7, 6, 8, 21]
Input: Solution.solution({{0, 1, 0, 0, 0, 1}, {4, 0, 0, 3, 2, 0}, {0,
0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0,
0, 0}}) Output:
[0, 3, 2, 9, 14]
-- Python cases -- Input: solution.solution([[0, 2, 1, 0, 0], [0, 0, 0, 3, 4], [0, 0, 0, 0, 0], [0, 0, 0, 0,0], [0, 0, 0, 0, 0]]) Output:
[7, 6, 8, 21]
Input: solution.solution([[0, 1, 0, 0, 0, 1], [4, 0, 0, 3, 2, 0], [0,
0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0,
0, 0]]) Output:
[0, 3, 2, 9, 14]
my code is below:
import numpy as np
from fractions import Fraction
from math import gcd
def solution(M):
height = (len(M))
length = (len(M[0]))
M = np.array(M)
AB = []
#Find B
for i in range(0, height):
#if B = 1
if (sum(M[:,0])) == 0:
sumB = 1
if(M[i,0]) != 0:
B1 = Fraction((M[i,0]), (sum(M[i])))
B2 = Fraction((M[0,i]), (sum(M[0])))
B = B1 * B2
#Find sum(B) to infinity
sumB = (1/(1-B))
#Find A
boolean2 = 0
count = 0
index = []
for i in range (0, height):
if sum(M[i]) == 0:
if boolean2 == 0:
terminalstart = i
boolean = 0
boolean2 = 1
for j in range(0, height):
#if there is no A
if j==height-1 and boolean == 0:
index.append(i-terminalstart)
count +=1
if (M[j,i]) != 0:
boolean = 1
A1 = Fraction((M[j,i]), (sum(M[j])))
A = A1
if j!=0:
A2 = Fraction((M[0,j]), (sum(M[0])))
A = A1 * A2
#Find AB
AB.append(A*sumB)
#Find common denominators
x = []
y = []
for i in range (0,len(AB)):
x.append(AB[i].denominator)
lcm = 1
#change numerators to fit
for i in x:
lcm = lcm*i//gcd(lcm, i)
for i in range (0, len(AB)):
z = (lcm) / x[i]
#
z = float(z)
#
y.append(int((AB[i].numerator)*z))
#insert 0s
for i in range (0, count):
y.insert(index[i], 0)
#insert denominator
y.append(lcm)
return y
So the code and the questions are basically irrelevant, the main point is, my output (y) is exactly the same as the output in the examples, but when it runs in foo.bar it fails. To test it I used a code that simply returned the desired output in foo.bar and it worked for the test case that had this output:
def solution(M):
y = [0, 3, 2, 9, 14]
return y
So I know that since my code gets to the exact same array and data type for y in the python IDE it should work in google foo.bar, but for some reason its not. Any help would be greatly appreciated
edit:
I found a code online that works:
import numpy as np
# Returns indexes of active & terminal states
def detect_states(matrix):
active, terminal = [], []
for rowN, row in enumerate(matrix):
(active if sum(row) else terminal).append(rowN)
return(active,terminal)
# Convert elements of array in simplest form
def simplest_form(B):
B = B.round().astype(int).A1 # np.matrix --> np.array
gcd = np.gcd.reduce(B)
B = np.append(B, B.sum()) # append the common denom
return (B / gcd).astype(int)
# Finds solution by calculating Absorbing probabilities
def solution(m):
active, terminal = detect_states(m)
if 0 in terminal: # special case when s0 is terminal
return [1] + [0]*len(terminal[1:]) + [1]
m = np.matrix(m, dtype=float)[active, :] # list --> np.matrix (active states only)
comm_denom = np.prod(m.sum(1)) # product of sum of all active rows (used later)
P = m / m.sum(1) # divide by sum of row to convert to probability matrix
Q, R = P[:, active], P[:, terminal] # separate Q & R
I = np.identity(len(Q))
N = (I - Q) ** (-1) # calc fundamental matrix
B = N[0] * R * comm_denom / np.linalg.det(N) # get absorbing probs & get them close to some integer
return simplest_form(B)
When I compared the final answer from this working code to mine by adding the lines:
print(simplest_form(B))
print(type(simplest_form(B))
this is what I got
[ 0 3 2 9 14]
<class 'numpy.ndarray'>
array([ 0, 3, 2, 9, 14])
When I added the lines
y = np.asarray(y)
print(y)
print(type(y))
to my code this is what I got:
[ 0 3 2 9 14]
<class 'numpy.ndarray'>
array([ 0, 3, 2, 9, 14])
when they were both running the same test input. These are the exact same but for some reason mine doesn't work on foo.bar but his does. Am I missing something?
It turns out the
math.gcd(x, y)
function is not allowed in python 2. I just rewrote it as this:
def grcd(x, y):
if x >= y:
big = x
small = y
else:
big = y
small = x
bool1 = 1
for i in range(1, big+1):
while bool1 == 1:
if big % small == 0:
greatest = small
bool1 = 0
small-= 1
return greatest
Suppose a list of labels label_list = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2] (0 is class 0, 1 is class 1 and 2 is class 2) and X a list of lists (i.e. the features). Each list of X has a unique label from label_list associated.
How can I implement a function lambda in python to create the Function Zero-One?
EDIT
Here is an example of what X might look like :
X = [[0.4, 0.2, 0.232], [0.3434, 0.243985, 0.9834], [0.3434, 0.345, 0.198459], [0.34324, 0.909834, 0.93849], [0.983498, 0.9384938, 0.983489], [0.768759, 0.9384, 0.897629], [0.14739, 0.872847, 0.62874], [0.6874, 0.87387, 0.83787], [0.8723874, 0.873764, 0.7527648], [0.762, 0.2648, 0.768738]]
Be aware that C is the associated class, i is the number of the class (i.e. either 0, 1 or 2) and t is the t-th vector/list of X.
Please I need help with this code.I want mylist to retain values appended to it next time the function 'no_repeat_rule' is called. I'm pretty new to python. My code is below:
def no_repeat_rule(play_board):
mylist = list()
if seeds_left(play_board) == 2 and sum(play_board[:6])== 1:
mylist.append(play_board)
return mylist
Output of this code (in part) is:
...
[[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
Player 1 chose cup 0
[[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
Player 2 chose cup 6
[[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]]
...
what I want the function 'no_repeat_rule' to do is to grow mylist each time a player plays. I don't know if this is clear enough to get help?
The simplest thing to do would be to add another parameter in the function defintion, such that it looks like:
def no_repeat_rule(play_board, aList):
Before you call the function, declare a list outside of the function. Set this equal to the result of the function, and pass it as a parameter whenever you call the function. For instance:
x = list()
def no_repeat_rule(play_board, aList):
myList = aList
if seeds_left(play_board) == 2 and sum(play_board[:6])== 1:
myList.append(play_board)
return myList
x = no_repeat_rule(someBoardHere, x)
I believe this should work if I understand what you're asking. If not, please respond and I'll try something else.
what do you need is an object which is associated with the function. It calls attribute. It is very handy in python.
Your code may look like this:
def no_repeat_rule(play_board):
if not hasattr(no_repeat_rule,"mylist"):
no_repeat_rule.mylist = []
if seeds_left(play_board) == 2 and sum(play_board[:6])== 1:
no_repeat_rule.mylist.append(play_board)
return no_repeat_rule.mylist
I couldn't check this code, but it should work for local atributes. BTW it is for python 2.7
I'm working in python with symbulate for a probability course and running some simulations.
Setup: Two teams, A and B, are playing in a “best of n” game championship series, where n is an odd number. For this example, n=7, and the probability team A wins any individual game is 0.55. Approximate the probability that Team A wins the series, given that they win the first game.
Here is what I've got so far, which I think is along the right lines:
model = BoxModel([1, 0], probs=[0.55, .45], size=7, replace=True)
test = model.sim(10000)
for x in range(0,10000):
test1 = test[x]
if test1[0] == 1:
print (test1)
test1
The last two lines are where I'm having my difficulty. This 'for' and 'if' combination makes it so only the inputs that start with a '1' (i.e. Team A winning the first game) are displayed. I need to save these inputs into a table so that I can run some further testing on it.
How do I input the value of test1 into a table while those loops are running? Currently, test1 only outputs the x=10,000th value.
Edit: The "test" yields a list, 0-10000, of all the possible game outcomes. I need a list that only has the game outcomes that start with a "1".
Edit2: Output of "test" (before I run a "for" or "if") look like:
Index Result
0 (1, 1, 1, 0, 0, 1, 1)
1 (0, 1, 0, 1, 1, 0, 0)
2 (1, 1, 1, 1, 0, 1, 0)
3 (0, 0, 1, 1, 1, 1, 1)
4 (0, 0, 0, 0, 0, 0, 0)
5 (1, 1, 0, 1, 0, 0, 1)
6 (0, 0, 1, 0, 1, 1, 1)
7 (0, 1, 0, 0, 0, 0, 1)
8 (1, 1, 0, 1, 0, 1, 0)
... ...
9999 (1, 1, 0, 1, 0, 0, 0)
I need a "test' (or another variable) to contain something that looks EXACTLY like that, but only contains lines that start with "1".
So you're looking to store the results of each test? Why not store them in a list?
test1_results = []
for x in range(0,10000):
test1 = test[x]
# check if first element in sequence of game outcomes is a win for team A
if test1[0] == 1: # or '1' if you're expecting string
test1_results.append(test1)
You can the run print(test1_results) to print the entire list of results, but if you want to print the first n results, do print(test1_results[:n]).
If you want your if statement in there, you will have to slightly adjust the placement. What does your test object look like? Could you give us a small sample?
edit: updated if statement to reflect comment below
Based on your comment:
results_that_start_with_one = []
for result in test:
result_string = str(result)
if result_string[0] == "1":
results_that_start_with_one.append(result_string)
This iterates through each of your results in the list "test". It convert each to a string (i'm assuming they are some numeric value). It then takes the first character in the string, and asks if its a 1.