Difference between revisions of "W3911 Sudoku Server"

From Coder Merlin
(Created page with " Background A Sudoku board is made up of nine boxes. Boxes (container for nine cells) are listed from top-to-bottom, left-to-right indexed from zero. Cells are listed from top...")
 
m (Editorial review and minor corrections)
 
(39 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.
Boxes (container for nine cells) are listed from top-to-bottom, left-to-right indexed from zero.
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, 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.   
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 ==
* POST /games
{{RestAPI
    * Action: Creates a new game and associated board
|endpoint=POST /games?difficulty=<difficulty>
    * Payload: None
|action=Creates a new game and associated board
    * Response: Id uniquely identifying a game
|parameters=difficulty: easy {{Bar}} medium {{Bar}} hard {{Bar}} hell
     * Status code: 201 Created
|body=<span style="font-style: italic;">none</span>
* GET /games/<id>/cells
|statusCode=201 Created
     * Action: None
|response=ID (integer) of newly created game
     * Payload: None
     {"id":<id>}
    * Response: cells
|responseExample={"id": 728134}
    * Status code: 200 OK
|errors=400 Bad Request (difficulty specified doesn't match requirements)
* PUT /games/<id>/cells/<boxIndex>/<cellIndex>
}}
    * Action: Place specified value at in game at boxIndex, cellIndex
{{RestAPI
     * Payload: value (null for removing value)
|endpoint=GET /games/<id>/cells?filter=<filter>
     * Response: Nothing
|action=Returns the current cells for the specified game
     * Status: 204 No Content
|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
|exerciseID=1
|language=swift
|height=400
|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 {
     let position: Position
    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
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]

CoderMerlin™ Code Explorer: W0000 (1) 🟢


JSON Decoding[edit]

CoderMerlin™ Code Explorer: W0000 (2) 🟢


Issue Request from Client to Server[edit]

CoderMerlin™ Code Explorer: W0000 (3) 🟢


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)