Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd9e094e

Browse files
committed
Add page NestedListAssignment
1 parent299bdef commitd9e094e

File tree

4 files changed

+409
-15
lines changed

4 files changed

+409
-15
lines changed

‎core/chapters/c11_tic_tac_toe_project.py‎

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# flake8: NOQA E501
22
importast
3+
fromcopyimportdeepcopy
34
fromrandomimportchoice,randint
45
fromstringimportascii_uppercase
6+
fromtextwrapimportdedent
57
fromtypingimportList
68

79
fromcore.textimportExerciseStep,Page,MessageStep,Disallowed,VerbatimStep
10+
fromcore.utilsimportreturns_stdout
811

912

1013
defgenerate_board(board_type):
@@ -1304,3 +1307,269 @@ def generate_inputs(cls):
13041307
super_secret_number = '7'
13051308
13061309
"""
1310+
1311+
1312+
classNestedListAssignment(Page):
1313+
title="Nested List Assignment: Playing Moves on the Board"
1314+
1315+
classmodify_list_in_function(VerbatimStep):
1316+
"""
1317+
We've seen how to get input from the user, now let's use that to actually put pieces
1318+
on the board and play the game. For starters, try out this code:
1319+
1320+
__copyable__
1321+
__program_indented__
1322+
"""
1323+
1324+
predicted_output_choices= [
1325+
" ",
1326+
"X",
1327+
"' '",
1328+
"'X'",
1329+
"[' ']",
1330+
"['X']",
1331+
"[' ', ' ', ' ']",
1332+
"['X', ' ', ' ']",
1333+
"[' ', 'X', ' ']",
1334+
]
1335+
1336+
defprogram(self):
1337+
defplay_move(board,player):
1338+
board[1]=player
1339+
1340+
defplay_game():
1341+
game_board= [" "," "," "]
1342+
play_move(game_board,"X")
1343+
print(game_board)
1344+
1345+
play_game()
1346+
1347+
classnested_assignment_two_lines(VerbatimStep):
1348+
"""
1349+
Note how calling `play_move(game_board, 'X')` actually *modifies* `game_board` directly.
1350+
The variable `board` inside the call to `play_move` and
1351+
the variable `game_board` inside the call to `play_game` point to the same list object.
1352+
There's no copying. Python Tutor is good at showing this with arrows.
1353+
1354+
This also means that in this case there's no need for `play_move` to return anything,
1355+
it can just modify `board` and the caller (`play_game` in this case) will see the effect.
1356+
1357+
However, our board is two dimensional, represented by a nested list.
1358+
So we need to assign `player` to an element of an inner list, something like this:
1359+
1360+
__copyable__
1361+
__program_indented__
1362+
"""
1363+
1364+
defprogram(self):
1365+
defplay_move(board,player):
1366+
row=board[1]
1367+
row[0]=player
1368+
1369+
defplay_game():
1370+
board= [
1371+
[" "," "," "],
1372+
[" "," "," "],
1373+
[" "," "," "],
1374+
]
1375+
play_move(board,"X")
1376+
print(board)
1377+
1378+
play_game()
1379+
1380+
classnested_assignment_input(ExerciseStep):
1381+
r"""
1382+
These two lines:
1383+
1384+
row = board[1]
1385+
row[0] = player
1386+
1387+
can be combined into one:
1388+
1389+
board[1][0] = player
1390+
1391+
The two pieces of code are pretty much exactly equivalent. Python first evaluates
1392+
`board[1]` to *get* the inner list, while the `[0] = ...` sets an element of `board[1]`.
1393+
You can see the value of `board[1]` in Bird's Eye because it's an expression,
1394+
and you could actually replace it with any other expression.
1395+
1396+
Now you know how to set elements in nested lists, it's time to make this interactive!
1397+
Write your own version of `play_move` that takes input from the user
1398+
to determine where to play, instead of always playing at `board[1][0]`.
1399+
It should call `input()` twice, so the user can give the row and the column
1400+
as two separate numbers. Also, our users are not programmers, so they start counting from 1,
1401+
not 0.
1402+
1403+
For example, if the user types in these inputs:
1404+
1405+
2
1406+
1
1407+
1408+
that means they want to play a move in the second row and first column, which is the same
1409+
as our original example.
1410+
1411+
Here is some starting code:
1412+
1413+
__copyable__
1414+
def format_board(board):
1415+
first_row = ' '
1416+
for i in range(len(board)):
1417+
first_row += str(i + 1)
1418+
joined_rows = [first_row]
1419+
for i in range(len(board)):
1420+
joined_row = str(i + 1) + ''.join(board[i])
1421+
joined_rows.append(joined_row)
1422+
return "\n".join(joined_rows)
1423+
1424+
def play_game():
1425+
board = [
1426+
[' ', ' ', ' '],
1427+
[' ', ' ', ' '],
1428+
[' ', ' ', ' '],
1429+
]
1430+
print(format_board(board))
1431+
print('\nX to play:\n')
1432+
play_move(board, 'X')
1433+
print(format_board(board))
1434+
print('\nO to play:\n')
1435+
play_move(board, 'O')
1436+
print(format_board(board))
1437+
1438+
def play_move(board, player):
1439+
...
1440+
1441+
play_game()
1442+
1443+
This calls `play_move` twice so the user will need to enter two pairs of numbers.
1444+
Here's an example of what a 'game' should look like:
1445+
1446+
123
1447+
1
1448+
2
1449+
3
1450+
1451+
X to play:
1452+
1453+
2
1454+
1
1455+
123
1456+
1
1457+
2X
1458+
3
1459+
1460+
O to play:
1461+
1462+
1
1463+
3
1464+
123
1465+
1 O
1466+
2X
1467+
3
1468+
1469+
You don't need to use the provided code exactly, it's just to give you a feeling of what's happening.
1470+
The important thing is that your `play_move` function modifies the `board` argument correctly.
1471+
It doesn't need to return or print anything, that will not be checked.
1472+
1473+
You can assume that the user will always enter valid numbers. Later we will learn how to deal
1474+
with invalid inputs, like numbers out of range or inputs that aren't numbers at all.
1475+
"""
1476+
1477+
hints="""
1478+
Your function needs to call `input()` twice. Input isn't passed to `play_move` as an argument.
1479+
`input()` always returns a string.
1480+
A string that looks like a number is still a string, not a number.
1481+
List indices have to be numbers, not strings.
1482+
If the board is 3x3, the user might input 1, 2, or 3 for each coordinate.
1483+
What are the valid indices of a list of length 3?
1484+
You need to take the input of 1, 2, or 3 and turn it into 0, 1, or 2.
1485+
You also need to be able to handle bigger boards, like 9x9 or beyond.
1486+
You can't do maths with strings, only numbers.
1487+
How can you convert a string to a number?
1488+
Once you've got two numbers, you need to modify the nested list `board` with them.
1489+
The code for this has been shown to you above.
1490+
You just need to use the numbers from user input instead of the hardcoded 1 and 0.
1491+
You can use nested subscripting in one line, or do it in two steps.
1492+
"""
1493+
1494+
no_returns_stdout=True
1495+
1496+
defsolution(self):
1497+
defplay_move(board,player):
1498+
row=int(input())-1
1499+
col=int(input())-1
1500+
board[row][col]=player
1501+
1502+
returnplay_move
1503+
1504+
@classmethod
1505+
defwrap_solution(cls,func):
1506+
@returns_stdout
1507+
defwrapper(**kwargs):
1508+
board=kwargs["board"]=deepcopy(kwargs["board"])
1509+
1510+
defformat_board():
1511+
first_row=' '
1512+
foriinrange(len(board)):
1513+
first_row+=str(i+1)
1514+
joined_rows= [first_row]
1515+
foriinrange(len(board)):
1516+
joined_row=str(i+1)+''.join(board[i])
1517+
joined_rows.append(joined_row)
1518+
return"\n".join(joined_rows)
1519+
1520+
func(**kwargs)
1521+
print(format_board())
1522+
returnwrapper
1523+
1524+
@classmethod
1525+
defgenerate_inputs(cls):
1526+
return {
1527+
"stdin_input": [str(randint(1,3)),str(randint(1,3))],
1528+
"player":choice(ascii_uppercase),
1529+
"board":generate_board(choice(["row","col","diag"])),
1530+
}
1531+
1532+
tests= [
1533+
(
1534+
{
1535+
"stdin_input": ["2","1"],
1536+
"board": [
1537+
[" "," "," "],
1538+
[" "," "," "],
1539+
[" "," "," "],
1540+
],
1541+
"player":"X",
1542+
},
1543+
dedent("""\
1544+
<input: 2>
1545+
<input: 1>
1546+
123
1547+
1
1548+
2X
1549+
3
1550+
"""),
1551+
),
1552+
(
1553+
{
1554+
"stdin_input": ["1","3"],
1555+
"board": [
1556+
[" "," "," "],
1557+
["X"," "," "],
1558+
[" "," "," "],
1559+
],
1560+
"player":"O",
1561+
},
1562+
dedent("""\
1563+
<input: 1>
1564+
<input: 3>
1565+
123
1566+
1 O
1567+
2X
1568+
3
1569+
"""),
1570+
),
1571+
]
1572+
1573+
final_text="""
1574+
Brilliant! You're almost ready to put it all together, keep going!
1575+
"""

‎core/text.py‎

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
importast
44
importinspect
5+
importitertools
56
importre
67
importtraceback
78
fromabcimportABC,abstractmethod
@@ -74,7 +75,9 @@ def clean_program(program, cls):
7475
func=NoMethodWrapper(func)
7576
func=add_stdin_input_arg(func)
7677

77-
ifnotany(isinstance(node,ast.Return)fornodeinast.walk(ast.parse(source))):
78+
ifnotany(
79+
isinstance(node,ast.Return)fornodeinast.walk(ast.parse(source))
80+
)andnotgetattr(cls,"no_returns_stdout",False):
7881
func=returns_stdout(func)
7982

8083
no_weird_whitespace(program)
@@ -454,13 +457,14 @@ def _patch_streams(cls, func):
454457

455458
@classmethod
456459
defcheck_exercise(cls,submission,functionise=False):
457-
cls.test_exercise(cls.solution)
460+
solution=cls.wrap_solution(cls.solution)
461+
cls.test_exercise(solution,cls.test_values())
458462
inputs= [cls.generate_inputs()for_inrange(10)]
459-
expected_generated_results= [cls.solution(**inp)forinpininputs]
463+
expected_generated_results= [solution(**inp)forinpininputs]
460464

461465
iffunctionise:
462466
try:
463-
initial_names,func=make_function(submission,cls.solution)
467+
initial_names,func=make_function(submission,solution)
464468
exceptInvalidInitialCode:
465469
# There should be an exception in the usual output
466470
returnFalse
@@ -471,30 +475,38 @@ def check_exercise(cls, submission, functionise=False):
471475
initial_names["stdin_input"]=cls.stdin_input
472476

473477
try:
474-
expected_result=cls.solution(**initial_names)
478+
expected_result=solution(**initial_names)
475479
exceptException:
476480
traceback.print_exc()
477481
returndict(
478482
message="The values of your input variables are invalid, "
479483
"try using values like the example."
480484
)
481485
try:
482-
check_result(func,initial_names,expected_result)
486+
cls.check_result(func,initial_names,expected_result)
483487
except:
484488
# Assume that the user can tell that the output is wrong
485489
returnFalse
486490
else:
491+
submission=cls.wrap_solution(submission)
487492
func=cls._patch_streams(submission)
488493

489494
try:
490-
cls.test_exercise(func)
491-
forinp,resultinzip(inputs,expected_generated_results):
492-
check_result(func,inp,result)
495+
cls.test_exercise(
496+
func,
497+
itertools.chain(
498+
cls.test_values(),zip(inputs,expected_generated_results)
499+
),
500+
)
493501
exceptExerciseErrorase:
494502
returndict(message=str(e))
495503

496504
returnTrue
497505

506+
@classmethod
507+
defwrap_solution(cls,func):
508+
returnfunc
509+
498510
@abstractmethod
499511
defsolution(self,*args,**kwargs):
500512
raiseNotImplementedError
@@ -519,9 +531,13 @@ def test_values(cls):
519531
yieldinputs,result
520532

521533
@classmethod
522-
deftest_exercise(cls,func):
523-
forinputs,resultincls.test_values():
524-
check_result(func,inputs,result)
534+
deftest_exercise(cls,func,values):
535+
forinputs,resultinvalues:
536+
cls.check_result(func,inputs,result)
537+
538+
@classmethod
539+
defcheck_result(cls,func,inputs,result):
540+
check_result(func,inputs,result)
525541

526542
@classmethod
527543
defgenerate_inputs(cls):

‎tests/test_frontend.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,9 +424,6 @@ def predict_output(driver, editor, run_button, first_choice, second_choice):
424424
WebDriverWait(driver,5).until(text_to_be_present_in_element(locator,"This"))
425425
sleep(2)
426426

427-
print("Terminal HTML:")
428-
print(driver.find_element(*locator).get_attribute("outerHTML"))
429-
430427
# Check the choices
431428
choices=driver.find_elements_by_class_name("prediction-choice")
432429
assertlen(choices)==7

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp