Skip to content

Commit

Permalink
feat: add hint model, dio client and dtos for hint generation
Browse files Browse the repository at this point in the history
  • Loading branch information
thisissandipp committed Jul 27, 2024
1 parent d61b784 commit dbcdd18
Show file tree
Hide file tree
Showing 16 changed files with 723 additions and 13 deletions.
38 changes: 34 additions & 4 deletions lib/api/dio/sudoku_dio_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:sudoku/api/api.dart';
import 'package:sudoku/api/dtos/dtos.dart';
import 'package:sudoku/env/env.dart';
import 'package:sudoku/models/difficulty.dart';
import 'package:sudoku/models/sudoku.dart';
import 'package:sudoku/models/models.dart';

/// {@template sudoku_dio_client}
/// An implemetation of the [SudokuAPI] using [Dio] as the http client.
Expand All @@ -22,12 +21,15 @@ class SudokuDioClient extends SudokuAPI {
final Dio dioClient;

Map<String, String> get _headers => {
'x-api-key': Env.apiKey,
};
'x-api-key': Env.apiKey,
};

/// HTTP request path for creating sudoku
static const createSudokuPath = '/createSudokuFlow';

/// HTTP request path for generating hints
static const generateHintPath = '/generateHintFlow';

@override
Future<Sudoku> createSudoku({required Difficulty difficulty}) async {
try {
Expand Down Expand Up @@ -60,4 +62,32 @@ class SudokuDioClient extends SudokuAPI {
throw const SudokuAPIClientException();
}
}

@override
Future<Hint> generateHint({required Sudoku sudoku}) async {
try {
final (puzzle, solution) = sudoku.toRawData();
final response = await dioClient.post<Map<String, dynamic>>(
generateHintPath,
data: GenerateHintRequestDto(
data: GenerateHintRequest(puzzle: puzzle, solution: solution),
).toJson(),
options: Options(
contentType: Headers.jsonContentType,
headers: _headers,
),
);

if (response.data == null) {
throw const SudokuAPIClientException();
}

final responseDto = GenerateHintResponseDto.fromJson(response.data!);
return responseDto.result.toHint();
} on DioException catch (error) {
throw SudokuAPIClientException(error: error);
} catch (e) {
throw const SudokuAPIClientException();
}
}
}
1 change: 1 addition & 0 deletions lib/api/dtos/create_sudoku_response_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class CreateSudokuResponseDto extends Equatable {
@immutable
@JsonSerializable()
class CreateSudokuResponse extends Equatable {
/// {@macro create_sudoku_response}
const CreateSudokuResponse({
required this.puzzle,
required this.solution,
Expand Down
2 changes: 2 additions & 0 deletions lib/api/dtos/dtos.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export 'create_sudoku_request_dto.dart';
export 'create_sudoku_response_dto.dart';
export 'generate_hint_request_dto.dart';
export 'generate_hint_response_dto.dart';
56 changes: 56 additions & 0 deletions lib/api/dtos/generate_hint_request_dto.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';

part 'generate_hint_request_dto.g.dart';

/// {@template generate_hint_request_dto}
/// A DTO to send [GenerateHintRequest] object wrapped within `data`
/// for the generate hint http request.
/// {@endtemplate}
@immutable
@JsonSerializable(explicitToJson: true)
class GenerateHintRequestDto extends Equatable {
/// {@macro generate_hint_request_dto}
const GenerateHintRequestDto({
required this.data,
});

/// The data to be sent over via http.
final GenerateHintRequest data;

/// Converts [GenerateHintRequestDto] into [Map].
Map<String, dynamic> toJson() => _$GenerateHintRequestDtoToJson(this);

@override
List<Object?> get props => [data];
}

/// {@template generate_hint_request}
/// Model for the generate hint http request.
/// {@endtemplate}
@immutable
@JsonSerializable()
class GenerateHintRequest extends Equatable {
/// {@macro generate_hint_request}
const GenerateHintRequest({
required this.puzzle,
required this.solution,
});

/// Converts a [Map] object to a [GenerateHintRequest] instance.
factory GenerateHintRequest.fromJson(Map<String, dynamic> json) =>
_$GenerateHintRequestFromJson(json);

/// Current state of the puzzle.
final List<List<int>> puzzle;

/// Solution of the puzzle.
final List<List<int>> solution;

/// Converts [GenerateHintRequest] into [Map].
Map<String, dynamic> toJson() => _$GenerateHintRequestToJson(this);

@override
List<Object?> get props => [puzzle, solution];
}
39 changes: 39 additions & 0 deletions lib/api/dtos/generate_hint_request_dto.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 84 additions & 0 deletions lib/api/dtos/generate_hint_response_dto.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:sudoku/models/models.dart';

part 'generate_hint_response_dto.g.dart';

/// {@template generate_hint_response_dto}
/// A DTO to receive [GenerateHintResponse] object wrapped within `data`
/// from the generate hint http request.
/// {@endtemplate}
@immutable
@JsonSerializable()
class GenerateHintResponseDto extends Equatable {
/// {@macro generate_hint_response_dto}
const GenerateHintResponseDto({
required this.result,
});

/// Converts a [Map] json into a [GenerateHintResponseDto] instance.
factory GenerateHintResponseDto.fromJson(Map<String, dynamic> json) =>
_$GenerateHintResponseDtoFromJson(json);

/// The result to be received over via http.
final GenerateHintResponse result;

@override
List<Object?> get props => [result];
}

/// {@template generate_hint_response}
/// Model for the generate hint http response.
/// {@endtemplate}
@immutable
@JsonSerializable()
class GenerateHintResponse extends Equatable {
/// {@macro generate_hint_response}
const GenerateHintResponse({
required this.cell,
required this.entry,
required this.observation,
required this.explanation,
required this.solution,
});

/// Converts a [Map] into a [GenerateHintResponse] instance.
factory GenerateHintResponse.fromJson(Map<String, dynamic> json) =>
_$GenerateHintResponseFromJson(json);

/// The position of the cell.
final List<int> cell;

/// The number to be put in the cell.
final int entry;

/// The observation of the puzzle state for solving the cell.
final String observation;

/// Explanation of the puzzle, and how to determine the solution.
final String explanation;

/// The solution of the puzzle in a sentence.
final String solution;

/// Converts the [GenerateHintResponse] to [Hint].
Hint toHint() {
return Hint(
cell: Position(x: cell[0], y: cell[1]),
entry: entry,
observation: observation,
explanation: explanation,
solution: solution,
);
}

@override
List<Object?> get props => [
cell,
entry,
observation,
explanation,
solution,
];
}
43 changes: 43 additions & 0 deletions lib/api/dtos/generate_hint_response_dto.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions lib/api/sudoku_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ abstract class SudokuAPI {
/// Throws a [SudokuInvalidRawDataException] when there's an error
/// during converting the raw data into [Sudoku] object.
Future<Sudoku> createSudoku({required Difficulty difficulty});

/// Creates a [Hint] for the puzzle.
///
/// Sends a HTTP request to the sudoku backend, which utilises
/// Firebase genkit to generate a hint based on current state of the puzzle.
Future<Hint> generateHint({required Sudoku sudoku});
}

/// {@template sudoku_api_client_exception}
Expand Down
42 changes: 42 additions & 0 deletions lib/models/hint.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:sudoku/models/models.dart';

/// {@template hint}
/// Model for a sudoku puzzle hint.
/// {@endtemplate}
@immutable
class Hint extends Equatable {
/// {@macro hint}
const Hint({
required this.cell,
required this.entry,
required this.observation,
required this.explanation,
required this.solution,
});

/// Defines the position or the cell for the hint.
final Position cell;

/// The number to be put in the cell.
final int entry;

/// The observation of the puzzle state for solving the cell.
final String observation;

/// Explanation of the puzzle, and how to determine the solution.
final String explanation;

/// The solution of the puzzle in a sentence.
final String solution;

@override
List<Object?> get props => [
cell,
entry,
observation,
explanation,
solution,
];
}
1 change: 1 addition & 0 deletions lib/models/models.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export 'block.dart';
export 'difficulty.dart';
export 'hint.dart';
export 'json_map.dart';
export 'position.dart';
export 'sudoku.dart';
Expand Down
16 changes: 12 additions & 4 deletions lib/models/sudoku.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,17 @@ class Sudoku extends Equatable {
}

/// Converts the specified sudoku to raw data.
List<List<int>> toRawData() {
///
/// Returns a record of puzzle and solution.
(List<List<int>>, List<List<int>>) toRawData() {
final dimension = getDimesion();

final rawData = List<List<int>>.generate(
final rawPuzzleData = List<List<int>>.generate(
dimension,
(_) => List<int>.generate(dimension, (_) => -1),
);

final rawSolutionData = List<List<int>>.generate(
dimension,
(_) => List<int>.generate(dimension, (_) => -1),
);
Expand All @@ -127,10 +134,11 @@ class Sudoku extends Equatable {
final positionX = block.position.x;
final positionY = block.position.y;

rawData[positionX][positionY] = block.currentValue;
rawPuzzleData[positionX][positionY] = block.currentValue;
rawSolutionData[positionX][positionY] = block.correctValue;
}

return rawData;
return (rawPuzzleData, rawSolutionData);
}

/// Returns the list of [Block]s that belong to the same sub-grid as [block].
Expand Down
Loading

0 comments on commit dbcdd18

Please sign in to comment.