Difference between revisions of "W3911 Sudoku Server"

From Coder Merlin
m (Editorial review and minor corrections)
 
(24 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 ==
{{RestAPI
{{RestAPI
|endpoint=POST /games?difficulty=<difficulty>
|endpoint=POST /games?difficulty=<difficulty>
Line 18: Line 21:
     {"id":<id>}
     {"id":<id>}
|responseExample={"id": 728134}
|responseExample={"id": 728134}
|errors=400 Bad Request (difficulty specified doesn't match requirements)
}}
}}
{{RestAPI
{{RestAPI
|endpoint=GET /games/<id>/cells
|endpoint=GET /games/<id>/cells?filter=<filter>
|action=Returns the current cells for the specified game
|action=Returns the current cells for the specified game
|parameters=<span style="font-style: italic;">none</span>
|parameters=filter: all {{Bar}} repeated {{Bar}} incorrect
|body=<span style="font-style: italic;">none</span>
|body=<span style="font-style: italic;">none</span>
|statusCode=200 OK
|statusCode=200 OK
Line 76: Line 80:
               {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}
               {"position":{"boxIndex":1,"cellIndex":8},"value":8}]}
   ]}
   ]}
|errors=400 Bad Request (filter specified doesn't match requirements)
}}
}}
{{RestAPI
{{RestAPI
Line 81: Line 86:
|action=Place specified value at in game at boxIndex, cellIndex
|action=Place specified value at in game at boxIndex, cellIndex
|parameters=<span style="font-style: italic;">none</span>
|parameters=<span style="font-style: italic;">none</span>
|body=value (null for removing value)
|body=
  {"value":<value>}
|statusCode=200 OK
|statusCode=200 OK
|response=<span style="font-style: italic;">none</span>
|response=<span style="font-style: italic;">none</span>
|responseExample=<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 ===
== JSON Encoding ==
{{CodeExplorer
{{CodeExplorer
|exerciseID=1
|exerciseID=1
|mode=swift
|language=swift
|height=400
|height=400
|initialCode=import Foundation
|initialCode=import Foundation
Line 137: Line 147:
}
}
}}
}}
=== JSON Decoding ===
 
== JSON Decoding ==
{{CodeExplorer
{{CodeExplorer
|exerciseID=2
|exerciseID=2
|mode=swift
|language=swift
|height=400
|height=400
|initialCode=import Foundation
|initialCode=import Foundation
Line 222: Line 233:
}
}
}}
}}
=== Issue Request from Client to Server ===
 
== Issue Request from Client to Server ==
{{CodeExplorer
{{CodeExplorer
|exerciseID=3
|exerciseID=3
|mode=swift
|language=swift
|height=400
|height=400
|initialCode=import Foundation                                                                                                                                                                                                                           
|initialCode=import Foundation                                                                                                                                                                                                                           
Line 270: Line 282:
}
}
}}
}}
== 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)