Home

A03: Habitat Helper

Read the Prolog notes on this website, pages 182-188 in AI Blueprints, and the example source code below. Refer to pages 31-49 (according to footer) of the NWRA Minimum Standards for Wildlife Rehabilitation and pages 27-29 (section 68A-6.01215) in Florida Administrative Code 68A-6.

Write a Prolog program that determines whether/how birds can be assigned to available housing (cages). Each bird species has specific minimum requirements for housing, and each available housing has dimensions and ameneties (defined by codes like ch = artificial chimney, pi = piling for perching, etc.). Each house also supports a maximum number of birds of the same species, so depending on how many birds we have, more than one house may be required for that bird species. Various species may also cohabit (details given below). Refer to both the NWRA and Florida Code for minimum size requirements and multipliers for housing multiple birds.

Support at least 20 different bird species, spread evenly across songbirds, waterbirds, and raptors. Ignore requirements related to wading pools, and use the “Unlimited Activity” requirements from the tables.

Inputs and Outputs

In order to make the program somewhat usable by regular people, data will be provided to the system via two CSV files, and output will be written to two new CSV files. Any table cell with ‘?’ means the program will have to fill in that value with minimum size and code requirements (in the case of housing) or maximum bird count.

birds.csv

Species (lowercase) Count (or ?)
pigeon ?
doves 10

As CSV:

Species (lowercase),Count (or ?)
pigeon,?
doves,10

housing.csv

Name Species (or ?) Width (ft or ?) Length (ft or ?) Height (ft or ?) Codes (comma-separated, no spaces, lowercase; or ?)
house1 pigeons,doves 16 8 8 p,pi
house2 ? 16 8 8 p,pi
house3 ? ? ? ? ?
house4 ? ? ? ? ?

As CSV:

Name,Species (or ?),Width (ft or ?),Length (ft or ?),Height (ft or ?),"Codes (comma-separated, no spaces, lowercase; or ?)"
house1,"pigeons,doves",16,8,8,"p,pi"
house2,?,16,8,8,"p,pi"
house3,?,?,?,?,?
house4,?,?,?,?,?

Bird Cohabitation Rules

The following species may be in the same house. When computing minimum size requirements (e.g., “For each additional bird, increase original floor area by 10 percent.” as mentioned in the Florida Code), ignore the species of each bird and just calculate based on count.

  • Pelicans, gulls, storks
  • Herons, egrets, storks

Submission

Create a repo on bitbucket called csci431-a03 and add me (eckroth@cse.ohio-state.edu) as a reader.

Grading rubric

  • 5 pts: all requirements met
  • 4 pts: housing constraints are checked for a list of birds and houses, with bird counts and housing ‘codes,’ but ignoring cohabitation
  • 3 pts: housing constraints are checked for a list of birds and houses, ignoring count of birds and/or housing ‘codes’
  • 2 pts: housing constraints are checked for a single bird/house (non-recursive), possibly ignoring count of birds
  • 0-1 pts: solution (housing configuration) not found or code does not run

Example Prolog code: Habitat Helper

species(pigeon, 8, 16, 8, 16, [p,pi]).
species(dove, 4, 16, 8, 16, [pi,q]).

house_meets_req((Species, Count), (_, HSpecies, HW, HL, HH, HCodes)) :-
    species(Species, MaxCount, MinW, MinL, MinH, MinCodes),
    member(Species, HSpecies),
    { Count =< MaxCount },
    { MinW =< HW, MinL =< HL, MinH =< HH },
    subset(MinCodes, HCodes).

some_house_meets_req((Species, Count), [House|_]) :-
    print(House), nl,
    house_meets_req((Species, Count), House).
some_house_meets_req((Species, Count), [_|HRest]) :-
    some_house_meets_req((Species, Count), HRest).

Example Prolog code: Student Advisor

% features:
% - database of students and courses taken (plus grades received)
% - does student meet requirements for courses & grades for csci major
% - calculate current gpa
% - find courses still remaining (accounting for failed courses)
% - allow for "partially known" grade in current courses (e.g., "can't get better than a C+")
% - find best possible gpa upon graduation (accounting for courses in-progress and to be taken)
% - find min grade necessary in in-progress/remaining courses to meet requirements
% - find min grades necessary in in-progress/remaining courses to achieve specified min gpa
% - TODO: support for electives

:- use_module(library(clpr)).

requiredCourses([csci141, csci142, csci221, csci321]).

student(jane, [(csci141, 2.33), (csci142, 1.0), (csci221, G)]) :- { G < 2.0 }.

sumGpa([], 0).
sumGpa([(_, Grade)|Rest], Sum) :-
    { Grade >= 0.0, Grade =< 4.0 },
    sumGpa(Rest, RestSum),
    { Sum = Grade + RestSum }.

calcGpa([], 0.0).
calcGpa(Courses, Gpa) :-
    length(Courses, N),
    sumGpa(Courses, Sum),
    { Gpa = Sum / N }.

% student(Name, Courses), calcGpa([(csci321, G)|Courses], Gpa), { G =< 4.0, G >= 0.0 }, maximize(Gpa).

requiredCoursesMet([], _).
requiredCoursesMet([Req|ReqRest], Courses) :-
    member((Req, Grade), Courses),
    { Grade >= 1.0 },
    requiredCoursesMet(ReqRest, Courses).

meetsRequirements(Courses) :-
    calcGpa(Courses, Gpa),
    { Gpa >= 2.0 },
    requiredCourses(Required),
    requiredCoursesMet(Required, Courses).

finishRequirements(Courses, RemainingCourses) :-
    append(RemainingCourses, Courses, AllCourses),
    meetsRequirements(AllCourses), !.

findMinGrades([], []).
findMinGrades([(Course, G)|Rest], [(Course, GMin)|MinRest]) :-
    inf(G, GMin), !,
    findMinGrades(Rest, MinRest).

Example query:

student(Name, Courses), finishRequirements(Courses, RemainingCourses), findMinGrades(RemainingCourses, MinGrades).

CSV input/output for birds

:- use_module(library(clpr)).

read_birds_csv(Filename, Birds) :-
    csv_read_file(Filename, [_|Rows]),
    parse_birds_csv_rows(Rows, Birds).

parse_birds_csv_rows([], []).
parse_birds_csv_rows([row(BirdTmp, CountTmp)|Rest], [(Bird, Count)|ParseRest]) :-
    ( BirdTmp = '?' -> Bird = _ ; Bird = BirdTmp ),
    ( CountTmp = '?' -> Count = _ ; Count = CountTmp ),
    { Count >= 0, Count =< 100 },
    parse_birds_csv_rows(Rest, ParseRest).

And to write CSV files:

prepare_birds_csv([], []).
prepare_birds_csv([(Species, Count)|BRest], [row(Species, Count)|Rest]) :-
    prepare_birds_csv(BRest, Rest).

write_birds_csv(Filename, Data) :-
    prepare_birds_csv(Data, PreparedData),
    csv_write_file(Filename, PreparedData).

CSV input/output for houses

And for parsing housing.csv:

read_houses_csv(Filename, Houses) :-
    csv_read_file(Filename, Rows),
    parse_housing_csv_rows(Rows, Houses).

parse_housing_csv_rows([], []).
parse_housing_csv_rows([row(Name, SpeciesTmp, WTmp, LTmp, HTmp, CodesTmp)|RowsRest], [(Name, Species, W, L, H, Codes)|ParseRest]) :-
    ( WTmp = '?' -> W = _ ; W = WTmp ),
    ( LTmp = '?' -> L = _ ; L = LTmp ),
    ( HTmp = '?' -> H = _ ; H = HTmp ),
    ( CodesTmp = '?' -> Codes = _ ; split_string(CodesTmp, ',', '', CodesStrSplit), maplist(atom_string, Codes, CodesStrSplit) ),
    ( SpeciesTmp = '?' -> Species = _ ; split_string(SpeciesTmp, ',', '', SpeciesStrSplit), maplist(atom_string, Species, SpeciesStrSplit) ),
    parse_housing_csv_rows(RowsRest, ParseRest).

And writing:

prepare_houses_csv([], []).
prepare_houses_csv([(HouseName, HouseSpecies, HW, HL, HH, HCodes)|HRest], [row(HouseName, HouseSpeciesStr, MinHW, MinHL, MinHH, HCodesStr)|Rest]) :-
    length(HouseSpecies, _), !,
    atomics_to_string(HouseSpecies, ',', HouseSpeciesStr),
    atomics_to_string(HCodes, ',', HCodesStr),
    inf(HW, MinHW), inf(HL, MinHL), inf(HH, MinHH),
    prepare_houses_csv(HRest, Rest).

write_houses_csv(Filename, Data) :-
    prepare_houses_csv(Data, PreparedData),
    csv_write_file(Filename, PreparedData).

Note, to get 4/5 or 5/5 points, you’ll need to keep track of # of birds in each house. You’ll need to update the house data structures to keep track of count per species for HouseSpecies: [(pigeons,5), (dove,3)], etc. You don’t necessarily need to change the CSV reading/writing code, as you can just avoid storing this info in the CSV files.

CSCI 431 material by Joshua Eckroth is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. Source code for this website available at GitHub.