%%%%%%%%%%%%%%%%
%Knowledge Base%
%%%%%%%%%%%%%%%%

%Weighting Points
temp_pts(4.4).
cond_pts(8.3).
humid_pts(0.1).
wind_pts(3.7).
max_pts(50).

%Ideal Conditions
temp_ideal(70).
dewpoint_ideal(60).
wind_ideal(0).

%Forecast condition points
weather_pts(condition(sunny), 0).
weather_pts(condition(partly_cloudy), 0).
weather_pts(condition(overcast), 1).
weather_pts(condition(weather(rain)), 5).
weather_pts(condition(weather(snow)), 5).
weather_pts(condition(weather(sleet)), 5).
weather_pts(condition(weather(graupel)), 5).
weather_pts(condition(weather(lightning)), 7).
weather_pts(condition(weather(tornado)), 5).
weather_pts(_, 0).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Forecast & Observation Variables%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Initial Observations
obs_cond(obs(sunny)).
obs_temp(obs(mild)).
obs_humid(obs(nhumid)).
obs_wind(obs(calm)).

%Second Observations
obs2_cond(_, NCond):-
    random(1, 8, 1),
    get_cond(GCond),
    cond_to_obs(GCond, NCond).
obs2_cond(Cond, NCond):-
    cond_to_obs(Cond, NCond).

obs2_temp(_, NTemp, GTemp):-
	random(1, 8, 1),
    temp(GTemp),
    temp_to_obs(GTemp, NTemp).
obs2_temp(Temp, NTemp, Temp):-
    temp_to_obs(Temp, NTemp).

obs2_humid(_, Temp, NDew):-
    random(1, 8, 1),
    dewpoint(GDew, Temp),
    dew_to_obs(GDew, NDew).
obs2_humid(Dew, _, NDew):-
    dew_to_obs(Dew, NDew).

obs2_wind(_, NWind):-
    random(1, 8, 1),
    wind(GWind),
    wind_to_obs(GWind, NWind).
obs2_wind(Wind, NWind):-
    wind_to_obs(Wind, NWind).

%Variables to observations
cond_to_obs(condition(sunny), obs(sunny)).
cond_to_obs(condition(partly_cloudy), obs(cloudy(partly(dark)))):-
    random(1, 4, 1).
cond_to_obs(condition(partly_cloudy), obs(cloudy(partly(light)))).
cond_to_obs(condition(overcast), obs(cloudy(overcast(dark)))):-
    random(1, 4, 1).
cond_to_obs(condition(overcast), obs(cloudy(overcast(light)))).
cond_to_obs(condition(weather(_)), obs(precip(nclearing))):-
    random(1, 4, 1).
cond_to_obs(condition(weather(_)), obs(precip(clearing))).

temp_to_obs(Temp, obs(vcold)):-
    Temp < 50.
temp_to_obs(Temp, obs(cold)):-
    Temp < 60,
    Temp >= 50.
temp_to_obs(Temp, obs(mild)):-
    Temp < 80,
    Temp >= 60.
temp_to_obs(Temp, obs(warm)):-
    Temp < 90,
    Temp >= 80.
temp_to_obs(Temp, obs(vwarm)):-
    Temp >= 90.

dew_to_obs(Dew, obs(nhumid)):-
    Dew < 60.
dew_to_obs(Dew, obs(humid)):-
    Dew >= 60,
    Dew < 70.
dew_to_obs(Dew, obs(vhumid)):-
    Dew >= 70.

wind_to_obs(Wind, obs(calm)):-
    Wind < 5.
wind_to_obs(Wind, obs(breezy)):-
    Wind >= 5,
    Wind < 15.
wind_to_obs(Wind, obs(windy)):-
    Wind >= 15.

%Values to conditions
get_cond(Output):-
    random(0, 100, Random),
    get_cond(Random, Output).
get_cond(Random, Output):-
    Random < 30,
    Output = condition(sunny).
get_cond(Random, Output):-
    Random < 75,
    Output = condition(partly_cloudy).
get_cond(Random, Output):-
    Random < 82,
    Output = condition(overcast).
get_cond(Random, Output):-
    Random < 85,
    Output = condition(weather(rain)).
get_cond(Random, Output):-
    Random < 90,
    Output = condition(weather(lightning)).
get_cond(Random, Output):-
    Random < 95,
    Output = condition(weather(snow)).
get_cond(Random, Output):-
    Random < 96,
    Output = condition(weather(graupel)).
get_cond(Random, Output):-
    Random < 100,
    Output = condition(weather(sleet)).
get_cond(Random, Output):-
    Random < 101,
    Output = condition(weather(tornado)).


%Forecast
cond(Output) :-
    get_cond(Output).
temp(Temp):-
    random(50, 100, Temp).
dewpoint(Dew, Temp):-
    random(35, Temp, Dew).
wind(Wind):-
    random(0, 20, Wind).
pop(Pop, condition(weather(_))):-
    random(30, 100, Pop).
pop(Pop, _):-
    random(0, 29, Pop).

%%%%%%%%%%%%%%%%%
%Variable Checks%
%%%%%%%%%%%%%%%%%

%Does a forecast needs to be checked?
forecast(cond, [obs(cloudy(overcast(light))), obs((cloudy(partly(dark)))), obs(precip(clearing))]).
forecast(temp, [obs(warm), obs(cold)]).
forecast(dewpoint, [obs(humid)]).
forecast(wind, [obs(breezy)]).
forecast(pop, [cond(weather(_))]).

%Is a certain variable good or bad for a picnic?
bad([condition(overcast), condition(weather(lightning)), condition(weather(tornado)), condition(weather(snow)), condition(weather(graupel)),
	condition(weather(rain)), condition(weather(sleet)), obs(cloudy(overcast(dark))),
	obs(precip(nclearing)), obs(vwarm), obs(vcold), obs(vhumid), obs(windy)]).

is_good(Variable) :-
    bad(List),
	\+ member(Variable, List).

%Check temperature forecast
check(temp, Obs, _, good):-
    forecast(temp, TempList),
    \+ member(Obs, TempList).
check(temp, Obs, Forecast, good) :-
	forecast(temp, TempList),
    member(Obs, TempList),
    Forecast >= 50,
    Forecast < 90,
    write("The current temperature is questionable..."), nl,
    write("The temperature is now forecasted to be "), write(Forecast),
    	write("°, which is good for a picnic"), nl, nl.
check(temp, _, Forecast, bad):-
     write("The current temperature is questionable..."), nl,
    write("The temperature is now forecasted to be "), write(Forecast),
    	write("°, which is not good for a picnic"), nl, nl.

%Check dewpoint forecast
check(dew, Obs, _, good):-
	forecast(dewpoint, DewList),
    \+ member(Obs, DewList).
check(dew, _, Forecast, good):-
	Forecast < 70,
    write("The current humidity feels questionable..."), nl,
    write("The dewpoint is now forecasted to be "), write(Forecast),
    	write("°, which is good for a picnic"), nl, nl.
check(dew, _, Forecast, bad):-
     write("The current humidity feels questionable..."), nl,
    write("The dewpoint is now forecasted to be "), write(Forecast),
    	write("°, which is notgood for a picnic"), nl, nl.

%Check wind forecast
check(wind, Obs, _, good):-
	forecast(wind, WindList),
    \+ member(Obs, WindList).
check(wind, _, Forecast, good):-
	Forecast < 15,
	write("The current wind is questionable..."), nl,
    write("The wind is now forecasted to be "), write(Forecast),
    	write("MPH, which is good for a picnic"), nl, nl.
check(wind, _, Forecast, bad):-
	write("The current wind is questionable..."), nl,
    write("The wind is now forecasted to be "), write(Forecast),
    	write("MPH, which is not good for a picnic"), nl, nl.
%Check conditions forecast
check(cond, Obs, _, _, good):-
    is_good(Obs),
	forecast(cond, CondList),
    \+ member(Obs, CondList).
check(cond, Obs, Forecast, Pop, good):-
    is_good(Obs),
    check(pop, Pop, good),
    parse_obs(cond, Forecast, NForecast),
    write("The current weather conditions are questionable..."), nl,
    write("The weather conditions are now forecasted to be "), write(NForecast),
    	write(", which is good for a picnic"), nl, nl.
check(cond, _, Forecast, _, bad):-
    parse_obs(cond, Forecast, NForecast),
    write("The current weather conditions are questionable..."), nl,
    write("The weather conditions are now forecasted to be "), write(NForecast),
    	write(", which is not good for a picnic"), nl, nl.

%Check pop forecast
check(pop, Pop, good):-
	Pop < 50.
check(pop, Pop, bad):-
	Pop >= 50.


	
%%%%%%%%%%%%%%%%%%%
%Parsing variables%
%%%%%%%%%%%%%%%%%%%

%Clouds
parse_clouds(Input, Output):-
	Input = overcast(dark),
	Output = "overcast with dark clouds".
parse_clouds(Input, Output):-
	Input = overcast(light),
	Output = "overcast with light clouds".
parse_clouds(Input, Output):-
	Input = partly(dark),
	Output = "partly cloudy with dark clouds".
parse_clouds(Input, Output):-
	Input = partly(light),
	Output = "partly cloudy with light clouds".

%Precip
parse_precip(Input, Output):-
	Input = clearing,
	Output = "precipitating with distant clearing".
parse_precip(Input, Output):-
	Input = nclearing,
	Output = "precipitating with no distant clearing".
	
%Temperatures
parse_obs(temp, Input, Output):-
    Input = obs(Temp),
	parse_obs(temp, Temp, Output).
parse_obs(temp, Input, Output):-
	Input = vwarm,
	Output = "very warm".
parse_obs(temp, Input, Output):-
	Input = warm,
	Output = "warm".
parse_obs(temp, Input, Output):-
	Input = mild,
	Output = "mild".
parse_obs(temp, Input, Output):-
	Input = cold,
	Output = "cold".
parse_obs(temp, Input, Output):-
	Input = vcold,
	Output = "very cold".
	
%Humidity
parse_obs(humid, Input, Output):-
	Input = obs(Dew),
    parse_obs(humid, Dew, Output).
parse_obs(humid, Input, Output):-
	Input = vhumid,
	Output = "high".
parse_obs(humid, Input, Output):-
	Input = humid,
	Output = "moderate".
parse_obs(humid, Input, Output):-
	Input = nhumid,
	Output = "low".
	
%Wind
parse_obs(wind, Input, Output):-
	Input = obs(Wind),
    parse_obs(wind, Wind, Output).
parse_obs(wind, Input, Output):-
	Input = windy,
	Output = "windy".
parse_obs(wind, Input, Output):-
	Input = breezy,
	Output = "breezy".
parse_obs(wind, Input, Output):-
	Input = calm,
	Output = "calm".
	
%Conditions
parse_obs(cond, Input, Output):-
    Input = obs(Cond),
    parse_obs(cond, Cond, Output).
parse_obs(cond, Input, Output):-
	Input = condition(precip(Precip)),
	parse_precip(Precip, Output).
parse_obs(cond, Input, Output):-
	Input = precip(Precip),
	parse_precip(Precip, Output).
parse_obs(cond, Input, Output):-
	Input = cloudy(Cloud),
	parse_clouds(Cloud, Output).
parse_obs(cond, Input, Output):-
    Input = condition(weather(Output)).
parse_obs(cond, Input, Output):-
	Input = condition(partly_cloudy),
    Output = "partly cloudy".
parse_obs(cond, Input, Output):-
    Input = condition(Output).
parse_obs(cond, Input, Output):-
    Input = Output.

evaluate_score(Score, good):-
    max_pts(Max),
   	Score =< Max.
evaluate_score(Score, bad):-
    max_pts(Max),
    Score > Max.

dew_pts(Forecast, Ideal, 0):-
    Forecast =< Ideal.
dew_pts(Forecast, Ideal, Output):-
    Output is (abs(Ideal - Forecast)/2).

evaluate_pts(Temp, Dew, Cond, Pop, Wind, Result):-
	%Get ideal variables
    temp_ideal(Itemp),
    dewpoint_ideal(Idew),
    wind_ideal(Iwind),
    
    %Get variable weights
    temp_pts(Ptemp),
    cond_pts(Pcond),
    humid_pts(Pdew),
    wind_pts(Pwind),
    
    %Get variable differences
    Dtemp is (abs(Itemp - Temp)/2),
    Dwind is (abs(Iwind - Wind)/2),
    dew_pts(Dew, Idew, Ddew),
    weather_pts(Cond, Dcond),
    
    %Determine variable points
    Stemp is (Dtemp * Ptemp),
    Sdew is (Ddew * Pdew),
    Swind is (Dwind * Pwind),
	Scond is ((Dcond * (Pop / 100)) * Pcond),

	Sum is (Stemp + Sdew + Swind + Scond),
    evaluate_score(Sum, Result).
   
picnic:-
	%Current observations from knowledge base
    obs_cond(obs(Cond)),
	obs_temp(obs(Temp)),
	obs_humid(obs(Dew)),
	obs_wind(obs(Wind)),
    
    %Current forecast from knowledge base
    temp(FTemp),
    cond(FCond),
    dewpoint(FDew, FTemp),
    wind(FWind),
    pop(FPop, FCond),
    
    %Second observations
    obs2_temp(FTemp, NTemp, DTemp),
    obs2_cond(FCond, NCond),
    obs2_humid(FDew, DTemp, NDew),
    obs2_wind(FWind, NWind),
    
    %Second forecast
    temp(NFTemp),
    cond(NFCond),
    dewpoint(NFDew, NFTemp),
    wind(NFWind),
    pop(NFPop, NFCond),
    
    %That edge case
	
    %Execute scenario
    initial_observations(Cond, Temp, Dew, Wind, Result),
    initial_forecast(FCond, FTemp, FDew, FWind, FPop, Result, Result2),
	second_observations(NCond, NTemp, NDew, NWind, Result2, Result3),
	second_forecast(NCond, NFCond, NTemp, NFTemp, NDew, NFDew, NWind, NFWind, NFPop, Result3, FinalResult),
    end(FinalResult).
   
initial_observations(Cond, Temp, Dew, Wind, good):-
    is_good(Cond),
	parse_obs(cond, Cond, CondOut),
	parse_obs(temp, Temp, TempOut),
	parse_obs(humid, Dew, DewOut),
	parse_obs(wind, Wind, WindOut),
    write("Walking outside, the current weather looks good for a picnic later..."), nl,
    write("It is currently "), write(CondOut), write(" with "), write(TempOut),
		write(" temperatures, "), write(DewOut), write(" humidity, and "), write(WindOut), write(" winds"), nl, nl.
initial_observations(Cond, _, _, _, bad):-
    write(Cond), nl,
    write("Walking outside, the current weather does not look good for a picnic later...").

initial_forecast(Cond, Temp, Dew, Wind, Pop, good, good):-
    evaluate_pts(Temp, Dew, Cond, Wind, Pop, good),
    parse_obs(cond, Cond, NewCond),
    write("The current forecast looks good for a picnic later..."), nl,
    write("The forecast has a temperature of "), write(Temp), write("° with "),
    	write(NewCond), write(" conditions, "), write(Dew), write("° dewpoints, and "),
        write(Wind), write(" MPH winds with a "), write(Pop), write("% chance of precipitation."),nl, nl.    
initial_forecast(Cond, Temp, Dew, Wind, Pop, good, bad):-
    parse_obs(cond, Cond, NewCond),
    write("The current forecast does not look good for a picnic later..."), nl,
    write("The forecast has a temperature of "), write(Temp), write("° with "),
    	write(NewCond), write(" conditions, "), write(Dew), write("° dewpoints, and "),
        write(Wind), write(" MPH winds with a "), write(Pop), write("% chance of precipitation."),nl, nl.
initial_forecast(_, _, _, _, _, _, bad).

second_observations(Cond, Temp, Dew, Wind, good, none):-
    is_good(Cond),
    is_good(Temp),
    is_good(Dew),
    is_good(Wind),
    forecast(cond, CondList),
    forecast(temp, TempList),
    forecast(dewpoint, DewList),
    forecast(wind, WindList),
    (member(Cond, CondList); member(Temp, TempList); member(Dew, DewList);
    	member(Wind, WindList)),
    parse_obs(cond, Cond, CondOut),
	parse_obs(temp, Temp, TempOut),
	parse_obs(humid, Dew, DewOut),
	parse_obs(wind, Wind, WindOut),
    write("It is now time for the picnic..."), nl, nl,
    write("Walking outside, the weather looks worse than forecasted..."), nl,
    write("It is currently "), write(CondOut), write(" with "), write(TempOut),
		write(" temperatures, "), write(DewOut), write(" humidity, and "), 
    	write(WindOut), write(" winds"), nl, nl.
second_observations(Cond, Temp, Dew, Wind, good, good):-
    is_good(Cond),
    is_good(Temp),
    is_good(Dew),
    is_good(Wind),
	parse_obs(cond, Cond, CondOut),
	parse_obs(temp, Temp, TempOut),
	parse_obs(humid, Dew, DewOut),
	parse_obs(wind, Wind, WindOut),
    write("It is now time for the picnic..."), nl, nl,
    write("Walking outside, the weather still looks good for a picnic..."), nl,
    write("It is currently "), write(CondOut), write(" with "), write(TempOut),
		write(" temperatures, "), write(DewOut), write(" humidity, and "), write(WindOut), write(" winds"), nl, nl.
second_observations(Cond, Temp, Dew, Wind, good, bad):-
	parse_obs(cond, Cond, CondOut),
	parse_obs(temp, Temp, TempOut),
	parse_obs(humid, Dew, DewOut),
	parse_obs(wind, Wind, WindOut),
    write("It is now time for the picnic..."), nl, nl,
    write("Walking outside, the weather does not look good for a picnic anymore..."), nl,
    write("It is currently "), write(CondOut), write(" with "), write(TempOut),
		write(" temperatures, "), write(DewOut), write(" humidity, and "), write(WindOut), write(" winds"), nl, nl.
second_observations(_, _, _, _, _, bad).

second_forecast(Cond, FCond, Temp, FTemp, Dew, FDew, Wind, 
                FWind, FPop, none, good):-
	check(cond, Cond, FCond, FPop, good),
    check(temp, Temp, FTemp, good),
    check(dew, Dew, FDew, good),
    check(wind, Wind, FWind, good).   
second_forecast(Cond, FCond, Temp, FTemp, Dew, FDew, Wind, 
                FWind, FPop, none, bad):-
	check(cond, Cond, FCond, FPop, _),
    check(temp, Temp, FTemp, _),
    check(dew, Dew, FDew, _),
    check(wind, Wind, FWind, _).
second_forecast(_, _, _, _, _, _, _, _, _, good, good).
second_forecast(_, _, _, _, _, _, _, _, _, bad, bad).

end(good):-
    random(1, 1000, 1),
    write("The weather turned out to be great for the picnic, so you decide
           to go and have one!"), nl, nl,
    write("However..."), nl,
    write("You arrive to find the park destroyed by a tornado, and can no
           longer have your picnic :(").
end(good):-
    random(1, 1000, 2),
    write("The weather turned out to be great for the picnic, so you decide
           to go and have one!"), nl, nl,
    write("However..."), nl,
    write("You arrive to find the park swarming with several 
           orangutans that escaped from the zoo, so you can't have your picnic :(").
end(good):-
    write("The weather turned out to be great for the picnic, so you decide
           to go and have one!").
end(bad):-
    write("The weather did not turn out so great for the picnic, so you decided
           to just stay home").