Hone logo
Hone
Problems

Generic Constraints Solver in TypeScript

This challenge asks you to build a flexible and type-safe system for defining and solving constraints in TypeScript. Such a system is invaluable for building complex applications that rely on logical relationships between variables, like scheduling systems, configuration validators, or game AI. You will implement a generic solver that can handle various types of constraints.

Problem Description

You need to create a TypeScript class ConstraintSolver<T> that can accept a set of variables of type T and a collection of constraints. The solver should be able to determine if a given assignment of values to variables satisfies all the defined constraints.

Key Requirements:

  1. Genericity: The solver must be generic, meaning it should work with any type T for the variable values.
  2. Constraint Definition: You need to define a way to represent different types of constraints. Initially, focus on binary constraints (involving two variables). Examples could include:
    • Equality: variableA === variableB
    • Inequality: variableA !== variableB
    • Greater Than: variableA > variableB
    • Less Than: variableA < variableB
  3. Solver Logic: Implement a solve method that takes an assignment (a mapping from variable names to their values) and returns true if all constraints are satisfied by the assignment, and false otherwise.
  4. Type Safety: Leverage TypeScript's type system to ensure that constraints are defined correctly and that operations within constraints are type-safe.

Expected Behavior:

The ConstraintSolver should be initialized with a set of variable names. Constraints are then added to the solver. When the solve method is called with an assignment, it iterates through all added constraints, evaluates them against the provided assignment, and returns a single boolean indicating global satisfaction.

Edge Cases:

  • An assignment might be missing a variable that is part of a constraint. The solver should handle this gracefully (e.g., consider the constraint as unsatisfied or throw an error, depending on your design choice – clearly document your choice).
  • Constraints involving types that do not support the defined operators (e.g., trying to use > on strings if not intended).

Examples

Example 1: Simple Equality Constraint

Assume variables can be numbers.

type Assignment = Record<string, number>;

class ConstraintSolver<T> {
  private variables: string[];
  private constraints: Constraint<T>[] = [];

  constructor(variables: string[]) {
    this.variables = variables;
  }

  addConstraint(constraint: Constraint<T>): void {
    this.constraints.push(constraint);
  }

  solve(assignment: Record<string, T>): boolean {
    // ... implementation details ...
    return true; // Placeholder
  }
}

// Define a type for constraints (you'll need to flesh this out)
interface Constraint<T> {
  variables: [string, string]; // The names of the variables involved
  evaluate: (valA: T, valB: T) => boolean; // The logic of the constraint
}

// Example Usage:
const solver = new ConstraintSolver<number>(['a', 'b']);

const equalityConstraint: Constraint<number> = {
  variables: ['a', 'b'],
  evaluate: (valA, valB) => valA === valB,
};

solver.addConstraint(equalityConstraint);

const assignment1: Record<string, number> = { 'a': 10, 'b': 10 };
console.log(solver.solve(assignment1)); // Expected: true

const assignment2: Record<string, number> = { 'a': 10, 'b': 20 };
console.log(solver.solve(assignment2)); // Expected: false

Example 2: Mixed Constraints with Missing Variables

Assume variables can be strings.

// Using the same ConstraintSolver and Constraint interface as above

const solver = new ConstraintSolver<string>(['greeting', 'farewell']);

const isEqualConstraint: Constraint<string> = {
  variables: ['greeting', 'farewell'],
  evaluate: (valA, valB) => valA === valB,
};

const isDifferentConstraint: Constraint<string> = {
  variables: ['greeting', 'farewell'],
  evaluate: (valA, valB) => valA !== valB,
};

solver.addConstraint(isEqualConstraint);
solver.addConstraint(isDifferentConstraint); // This constraint is inherently unsatisfiable with itself

const assignment1: Record<string, string> = { 'greeting': 'hello', 'farewell': 'hello' };
console.log(solver.solve(assignment1)); // Expected: false (equality passes, inequality fails)

const assignment2: Record<string, string> = { 'greeting': 'hello' }; // 'farewell' is missing
console.log(solver.solve(assignment2)); // Expected: false (if you decide missing variables make constraints fail)

Example 3: Constraints with Different Variable Count (Optional Extension)

If you want to extend beyond binary constraints, consider how you'd represent and solve them. For this initial challenge, stick to binary.

Constraints

  • The ConstraintSolver class should be named ConstraintSolver<T>.
  • The type T must be a primitive type (number, string, boolean) or a union of such types for initial evaluation. You can consider complex objects if you extend the evaluate function to handle them.
  • The solve method should aim for reasonable performance, avoiding excessive recalculations.
  • The Constraint interface (or a similar mechanism) should clearly define the variables involved and the evaluation logic.
  • Your solution should be well-typed and leverage TypeScript's generic capabilities effectively.

Notes

  • Think carefully about how to represent the constraints themselves. An interface or an abstract class could be good starting points.
  • For the solve method, you'll need a way to retrieve values from the assignment for the variables involved in each constraint. Consider how to handle cases where a variable is not present in the assignment.
  • You might want to define a base Constraint class or interface and then create specific subclasses or implementations for different constraint types (e.g., EqualityConstraint, InequalityConstraint).
  • Consider how to ensure type safety when defining the evaluate function for a constraint. You might need to use type guards or other TypeScript features.
Loading editor...
typescript