Difference between revisions of "W3911 Sudoku Server"
From Coder Merlin
Jeff-strong (talk | contribs) m (Editorial review and minor corrections) |
|||
(38 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
Background | == Getting Started == | ||
See [[Vapor]] | |||
== Background == | |||
A Sudoku board is made up of nine boxes. | A Sudoku board is made up of nine boxes. | ||
The boxes (containers for nine cells) are listed from top to bottom, left to right, and indexed from zero. | |||
Cells are listed from top | Cells are listed from top to bottom, left to right, and indexed from zero. | ||
All valid payloads and responses must use well-formed JSON. | All valid payloads and responses must use well-formed JSON. | ||
“cells” is returned as follows: | The value of “cells” is returned as follows: | ||
“cells”: [[<nine values from top-left>], [<nine values from top-middle>], …] | “cells”: [[<nine values from top-left>], [<nine values from top-middle>], …] | ||
End Points | == End Points == | ||
{{RestAPI | |||
|endpoint=POST /games?difficulty=<difficulty> | |||
|action=Creates a new game and associated board | |||
|parameters=difficulty: easy {{Bar}} medium {{Bar}} hard {{Bar}} hell | |||
|body=<span style="font-style: italic;">none</span> | |||
|statusCode=201 Created | |||
|response=ID (integer) of newly created game | |||
{"id":<id>} | |||
|responseExample={"id": 728134} | |||
|errors=400 Bad Request (difficulty specified doesn't match requirements) | |||
}} | |||
{{RestAPI | |||
|endpoint=GET /games/<id>/cells?filter=<filter> | |||
|action=Returns the current cells for the specified game | |||
|parameters=filter: all {{Bar}} repeated {{Bar}} incorrect | |||
|body=<span style="font-style: italic;">none</span> | |||
|statusCode=200 OK | |||
|response= | |||
{"board": | |||
[{"cells":[{"position":{"boxIndex":<boxIndex>,"cellIndex":<cellIndex>},"value":<value>}... | |||
|responseExample= | |||
{"board": | |||
[{"cells":[{"position":{"boxIndex":0,"cellIndex":0},"value":0},{"position":{"boxIndex":0,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":0,"cellIndex":2},"value":2},{"position":{"boxIndex":0,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":0,"cellIndex":4},"value":4},{"position":{"boxIndex":0,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":0,"cellIndex":6},"value":6},{"position":{"boxIndex":0,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":0,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]} | |||
]} | |||
|errors=400 Bad Request (filter specified doesn't match requirements) | |||
}} | |||
{{RestAPI | |||
|endpoint=PUT /games/<id>/cells/<boxIndex>/<cellIndex> | |||
|action=Place specified value at in game at boxIndex, cellIndex | |||
|parameters=<span style="font-style: italic;">none</span> | |||
|body= | |||
{"value":<value>} | |||
|statusCode=200 OK | |||
|response=<span style="font-style: italic;">none</span> | |||
|responseExample=<span style="font-style: italic;">none</span> | |||
|errors= | |||
400 Bad Request (boxIndex is out of range 0 ... 8) | |||
400 Bad Request (cellIndex is out of range 0 ... 8) | |||
400 Bad Request (value is out of range 1 ... 9 or null) | |||
}} | |||
== JSON Encoding == | |||
{{CodeExplorer | {{CodeExplorer | ||
| | |exerciseID=1 | ||
| | |language=swift | ||
|height= | |height=400 | ||
|initialCode=import Foundation | |initialCode=import Foundation | ||
let board = Board() | |||
let encoder = JSONEncoder() | |||
guard let data = try? encoder.encode(board), | |||
let json = String(data: data, encoding: .utf8) else { | |||
fatalError("Failed to encode data into json.") | |||
} | |||
print(json) | |||
// Structure definitions | |||
struct Position: Codable { | |||
let boxIndex: Int | |||
let cellIndex: Int | |||
} | |||
struct Cell: Codable { | struct Cell: Codable { | ||
let position: Position | |||
let value: Int? | let value: Int? | ||
} | |||
struct Box: Codable { | |||
let cells: [Cell] | |||
init(boxIndex: Int) { | |||
var cells = [Cell]() | |||
for cellIndex in 0 ..< 9 { | |||
cells.append(Cell(position: Position(boxIndex: boxIndex, cellIndex: cellIndex), value: cellIndex)) | |||
} | |||
self.cells = cells | |||
} | |||
} | |||
struct Board: Codable { | |||
let board: [Box] | |||
init() { | |||
var board = [Box]() | |||
for boxIndex in 0 ..< 9 { | |||
board.append(Box(boxIndex: boxIndex)) | |||
} | |||
self.board = board | |||
} | |||
} | |||
}} | |||
== JSON Decoding == | |||
{{CodeExplorer | |||
|exerciseID=2 | |||
} | |language=swift | ||
|height=400 | |||
|initialCode=import Foundation | |||
let json = | |||
#""" | |||
{"board": | |||
[{"cells":[{"position":{"boxIndex":0,"cellIndex":0},"value":0},{"position":{"boxIndex":0,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":0,"cellIndex":2},"value":2},{"position":{"boxIndex":0,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":0,"cellIndex":4},"value":4},{"position":{"boxIndex":0,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":0,"cellIndex":6},"value":6},{"position":{"boxIndex":0,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":0,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, | |||
{"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, | |||
{"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, | |||
{"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, | |||
{"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, | |||
{"position":{"boxIndex":1,"cellIndex":8},"value":8}]} | |||
]} | |||
"""# | |||
let decoder = JSONDecoder() | |||
guard let data = json.data(using: .utf8), | |||
let board: Board = try? decoder.decode(Board.self, from: data) else { | |||
fatalError("Failed to decode json into board.") | |||
} | |||
dump(board) | |||
// Structure definitions | |||
struct Position: Codable { | |||
let boxIndex: Int | |||
let cellIndex: Int | |||
} | |||
struct Cell: Codable { | |||
let position: Position | |||
let value: Int? | |||
} | |||
struct Box: Codable { | |||
let cells: [Cell] | |||
} | |||
struct Board: Codable { | |||
let board: [Box] | |||
} | } | ||
}} | |||
== Issue Request from Client to Server == | |||
{{CodeExplorer | |||
|exerciseID=3 | |||
|language=swift | |||
|height=400 | |||
|initialCode=import Foundation | |||
import FoundationNetworking | |||
// Form the request | |||
var request = URLRequest(url: URL(string: "https://language-server.codermerlin.com/now")!) | |||
request.httpMethod = "GET" | |||
// Execute synchronously (uses extension in code below) | |||
let (data, response, error) = URLSession.shared.syncRequest(with: request) | |||
if let data = data, | |||
let string = String(data: data, encoding: .utf8) { | |||
print(string) | |||
} else if let error = error { | |||
print("Error: \(error)") | |||
} else { | |||
print("An unexpected error occurred.") | |||
} | |||
// Reference: https://stackoverflow.com/questions/26784315/can-i-somehow-do-a-synchronous-http-request-via-nsurlsession-in-swift | |||
let | extension URLSession { | ||
func syncRequest(with request: URLRequest) -> (Data?, URLResponse?, Error?) { | |||
var data: Data? | |||
var response: URLResponse? | |||
var error: Error? | |||
let dispatchGroup = DispatchGroup() | |||
let task = dataTask(with: request) { | |||
data = $0 | |||
response = $1 | |||
error = $2 | |||
dispatchGroup.leave() | |||
} | |||
dispatchGroup.enter() | |||
task.resume() | |||
dispatchGroup.wait() | |||
return (data, response, error) | |||
} | |||
} | } | ||
}} | }} | ||
== Server-side Query String Access == | |||
<syntaxhighlight lang="swift"> | |||
let difficulty: String? = request.query["difficulty"] | |||
</syntaxhighlight> | |||
== Server-side Content Access for JSON == | |||
<syntaxhighlight lang="swift"> | |||
app.put("value") { req -> String in | |||
struct CellValue: Decodable { | |||
let value: Int? | |||
} | |||
let cellValue = try req.content.decode(CellValue.self) | |||
return "Done" | |||
} | |||
</syntaxhighlight> | |||
== Server-side Parameter Access and Error Handling == | |||
<syntaxhighlight lang="swift"> | |||
guard let boxIndex = req.parameters.get("boxIndex", as: Int.self), | |||
let cellIndex = req.parameters.get("cellIndex", as: Int.self) else { | |||
throw Abort(.badRequest, reason: "boxIndex and cellIndex MUST be integers") | |||
} | |||
</syntaxhighlight> | |||
== Server-side Returning "No Content" == | |||
<syntaxhighlight lang="swift"> | |||
return Response(status: .noContent) | |||
</syntaxhighlight> |
Latest revision as of 08:59, 11 February 2023
Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder
Getting Started[edit]
See Vapor
Background[edit]
A Sudoku board is made up of nine boxes. The boxes (containers for nine cells) are listed from top to bottom, left to right, and indexed from zero. Cells are listed from top to bottom, left to right, and indexed from zero. All valid payloads and responses must use well-formed JSON. The value of “cells” is returned as follows: “cells”: [[<nine values from top-left>], [<nine values from top-middle>], …]
End Points[edit]
POST /games?difficulty=<difficulty>
Creates a new game and associated board
Parameters:
difficulty: easy | medium | hard | hell
Body: none
Status code: 201 Created
Response: ID (integer) of newly created game {"id":<id>}
{"id": 728134}
Errors:
400 Bad Request (difficulty specified doesn't match requirements)
GET /games/<id>/cells?filter=<filter>
Returns the current cells for the specified game
Parameters:
filter: all | repeated | incorrect
Body: none
Status code: 200 OK
Response: {"board": [{"cells":[{"position":{"boxIndex":<boxIndex>,"cellIndex":<cellIndex>},"value":<value>}...
{"board": [{"cells":[{"position":{"boxIndex":0,"cellIndex":0},"value":0},{"position":{"boxIndex":0,"cellIndex":1},"value":1}, {"position":{"boxIndex":0,"cellIndex":2},"value":2},{"position":{"boxIndex":0,"cellIndex":3},"value":3}, {"position":{"boxIndex":0,"cellIndex":4},"value":4},{"position":{"boxIndex":0,"cellIndex":5},"value":5}, {"position":{"boxIndex":0,"cellIndex":6},"value":6},{"position":{"boxIndex":0,"cellIndex":7},"value":7}, {"position":{"boxIndex":0,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}, {"cells":[{"position":{"boxIndex":1,"cellIndex":0},"value":0},{"position":{"boxIndex":1,"cellIndex":1},"value":1}, {"position":{"boxIndex":1,"cellIndex":2},"value":2},{"position":{"boxIndex":1,"cellIndex":3},"value":3}, {"position":{"boxIndex":1,"cellIndex":4},"value":4},{"position":{"boxIndex":1,"cellIndex":5},"value":5}, {"position":{"boxIndex":1,"cellIndex":6},"value":6},{"position":{"boxIndex":1,"cellIndex":7},"value":7}, {"position":{"boxIndex":1,"cellIndex":8},"value":8}]} ]}
Errors:
400 Bad Request (filter specified doesn't match requirements)
PUT /games/<id>/cells/<boxIndex>/<cellIndex>
Place specified value at in game at boxIndex, cellIndex
Parameters: none
Body: {"value":<value>}
Status code: 200 OK
Response: none
none
Errors:
400 Bad Request (boxIndex is out of range 0 ... 8) 400 Bad Request (cellIndex is out of range 0 ... 8) 400 Bad Request (value is out of range 1 ... 9 or null)
JSON Encoding[edit]
JSON Decoding[edit]
Issue Request from Client to Server[edit]
Server-side Query String Access[edit]
let difficulty: String? = request.query["difficulty"]
Server-side Content Access for JSON[edit]
app.put("value") { req -> String in
struct CellValue: Decodable {
let value: Int?
}
let cellValue = try req.content.decode(CellValue.self)
return "Done"
}
Server-side Parameter Access and Error Handling[edit]
guard let boxIndex = req.parameters.get("boxIndex", as: Int.self),
let cellIndex = req.parameters.get("cellIndex", as: Int.self) else {
throw Abort(.badRequest, reason: "boxIndex and cellIndex MUST be integers")
}
Server-side Returning "No Content"[edit]
return Response(status: .noContent)