#include <iostream>
#include <functional>
#include <string>
#include <random>
using namespace std;
template<typename T>
class Attemptor{
public:
struct Actions{
function<T()> in;
function<bool(T)> onCheck;
function<void(T)> onSuccess;
function<void(T)> onPartialFailure;
function<void(T)> onTotalFailure;
function<void()> onNoMoreAttemptsLeft;
};
Attemptor(Actions actions, size_t maxFailures)
: _actions(actions), _maxFailures(maxFailures), _failures(0){}
bool canAttempt() const{
return !_success && _maxFailures > _failures;
}
bool isDone() const{
return _done;
}
bool success() const{
return _success;
}
bool attempt(){
if(!canAttempt()){
_actions.onNoMoreAttemptsLeft();
return false;
};
T in_result = _actions.in();
if(_actions.onCheck(in_result)){
_actions.onSuccess(in_result);
return finishWithSuccessEq(true);
}
_failures += 1;
if(canAttempt()) _actions.onPartialFailure(in_result);
else {
_actions.onTotalFailure(in_result);
return finishWithSuccessEq(false);
}
}
private:
bool finishWithSuccessEq(bool val){
_done = true;
return _success = val;
}
bool _done = false;
bool _success = false;
const Actions _actions;
const size_t _maxFailures;
size_t _failures;
};
template<typename T>
auto read(istream &in = cin){
T var;
in >> var;
return var;
}
int main() {
size_t min = 1, max = 100, attempts = 10;
default_random_engine gen(0xCafeBabe);
uniform_int_distribution<> dist(min, max);
auto number = dist(gen);
using attemptor_type = Attemptor<int>;
using actions_type = typename attemptor_type::Actions;
auto attemptor = attemptor_type(
actions_type{
[]{ return read<int>(); },
[number](int in){ return in == number; },
[](int res){cout << "Success with " << res << "!" << endl;},
[number](int res){cout << (res<number? "<":">") << endl;},
[](int res){cout << "Total failure with " << res << "!" << endl;},
[]{cout << "No more attempts left!" << endl;}
}, attempts
);
while(!attemptor.attempt());
return 0;
}