//****************************************************************// // // // Copyright (c) 1997. // // Dr. Michael A. Redmond // // // // This software may not be distributed further without permission from // // Dr Michael A. Redmond. // // // // This software is distributed WITHOUT ANY WARRANTY. No claims are made // // as to its functionality or purpose. // // // // Title: Tw_Link.cpp // Description: abstraction for twixt link, includes two pegs and color // Author: Dr Michael A. Redmond // // Date: 3/07/97 // // // // //****************************************************************// #include #include #include "point.h" #include "tw-color.h" #include "tw-peg.h" #include "tw-link.h" #include "range.h" // create a link given two pegs if compatible colors and appropriate // distance Tw_Link::Tw_Link(const Tw_Peg& peg1, const Tw_Peg& peg2) { // efficiency would be a little better if values were // initialized using base-member initialization list // instead of assigned using mutators // but doing that would require putting the below checking // into a separate global function // So instead, I need to create a default Tw_Peg // constructor which will handle the initial creation // of the peg objects at the start of this link // constructor // this comment also applies to some of the other link // constructors below Tw_Color color1 = peg1.WhatColor(); Tw_Color color2 = peg2.WhatColor(); // make sure pegs are appropriate distance apart bool rightdist = peg1.LegalLinkOffset(peg2); clog << "rightdist: " << rightdist << endl; if (rightdist) { // make sure both pegs have the same color if (color1 == color2) { // make sure its not empty if (color1 != empty) { SetStartPeg(peg1); SetEndPeg(peg2); SetLinkColor(color1); } // end if not empty else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are empty locations " << endl; } } // end if color1 == color2 else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they do not have the same color" << endl; } } // end if rightdist else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are the wrong distance apart" << endl; } } // create a link given two pegs and a color, if compatible colors and appropriate // distance Tw_Link::Tw_Link(const Tw_Peg& peg1, const Tw_Peg& peg2, const Tw_Color& color) { Tw_Color color1 = peg1.WhatColor(); Tw_Color color2 = peg2.WhatColor(); // make sure pegs are appropriate distance apart bool rightdist = peg1.LegalLinkOffset(peg2); if (rightdist) { // make sure both pegs have the same color which is the same as // passed if ((color1 == color2) && (color1 == color)) { // make sure its not empty if (color1 != empty) { SetStartPeg(peg1); SetEndPeg(peg2); SetLinkColor(color1); } // end if color1 empty else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are empty locations " << endl; } } // end if color1 == color2 etc else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since color(s) are not compatible with passed color" << endl; } // end second else } // end if rightdist else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are the wrong distance apart" << endl; } } // create a link given two points and a color, if appropriate // distance (no way to check colors - we create the pegs here) Tw_Link::Tw_Link(const Point& pt1, const Point& pt2, const Tw_Color& color) { Tw_Peg peg1(pt1,color); Tw_Peg peg2(pt2,color); // make sure pegs are appropriate distance apart bool rightdist = peg1.LegalLinkOffset(peg2); if (rightdist) { SetStartPeg(peg1); SetEndPeg(peg2); SetLinkColor(color); } else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are the wrong distance apart" << endl; } } // create a link given coordinates for two points and a color, if appropriate // distance (no way to check colors - we create the pegs here) Tw_Link::Tw_Link(const int startrow, const int startcol, const int endrow, const int endcol, const Tw_Color& color) { Tw_Peg peg1(startrow,startcol,color); Tw_Peg peg2(endrow,endcol,color); // make sure pegs are appropriate distance apart bool rightdist = peg1.LegalLinkOffset(peg2); if (rightdist) { SetStartPeg(peg1); SetEndPeg(peg2); SetLinkColor(color); } else { cerr << "Unable to create requested link between pegs: " << peg1 << peg2 << " since they are the wrong distance apart" << endl; } } // create a default link - no useful values, but available incase // data will not be avail until user interaction Tw_Link::Tw_Link() { Tw_Peg peg1(badrow,badcol,illegal); Tw_Peg peg2(badrow,badcol,illegal); SetStartPeg(peg1); SetEndPeg(peg2); SetLinkColor(illegal); } // find out link color Tw_Color Tw_Link::WhatColor() const { return GetLinkColor(); } // determine if two links are in conflict with each other // they are in conflict with each other if they would cross bool Tw_Link::Conflict(const Tw_Link& link2) const { bool conflict = false; // first see if there is any conceivable possibility of a collision // (if one link goes from 1,2 to 3,3 anf the other goes from 10,9 // to 8,8 then THERE IS NO WAY THAT THERE WILL BE A CONFLICT // avoid going through the expense of carefully checking if (!Nearby(link2)) { clog << *this << " not nearby " << link2 << " - no risk of conflict" << endl; conflict = false; } else { // links are somewhere near each other // calculate their slopes float slope1 = CalcSlope(); float slope2 = link2.CalcSlope(); // if slopes are equal the links are parallel and cannot conflict // (assuming they dont share end-points - which is already // taken into account - sharing end pt with same color is // fine (no conflict) and sharing end-pts with opponent // should be impossible // inexactness of floats is a problem in testing for equality // check for very close if (fabs(slope1 - slope2) < 0.0001) { clog << "slopes roughly equal between " << *this << " and " << link2 << " (" << slope1 << "," << slope2 << ") - little risk of conflict" << endl; conflict = false; } else { // not parallel - potentially could cross // first get y intercepts float interc1 = CalcIntercept(slope1); float interc2 = link2.CalcIntercept(slope2); // use algebra I to see if links intersect conflict = Intersect(slope1,interc1, link2, slope2, interc2); } // end last else } // end first else return conflict; } // YO - need to do static casts here to use real floats // calculate slope of a link using Algebra I float Tw_Link::CalcSlope() const { int start_row = GetStartPeg().GetPosition().GetRow(); int start_col = GetStartPeg().GetPosition().GetCol(); int end_row = GetEndPeg().GetPosition().GetRow(); int end_col = GetEndPeg().GetPosition().GetCol(); float float_start_row = static_cast(start_row); float float_start_col = static_cast(start_col); float float_end_row = static_cast(end_row); float float_end_col = static_cast(end_col); float slope = (float_start_row - float_end_row) / (float_start_col - float_end_col); return slope; } // YO - need to do static casts here to use real floats // calculate y intercept of a link using Algebra I // either start point or end point of link could be used float Tw_Link::CalcIntercept(const float slope) const { float start_row = static_cast(GetStartPeg().GetPosition().GetRow()); float start_col = static_cast(GetStartPeg().GetPosition().GetCol()); // subtract enough from y to get x down to zero float intercept = (start_row - (start_col * slope)); return intercept; } // determine if two links are anywhere near each other // returns true if they are near // be conservative because if they are not nearby we will do // no further checking for collisions // nearby if either start or end point of one link is // within either x or y range of other link bool Tw_Link::Nearby(const Tw_Link& link2) const { int start_row1 = GetStartPeg().GetPosition().GetRow(); int start_col1 = GetStartPeg().GetPosition().GetCol(); int end_row1 = GetEndPeg().GetPosition().GetRow(); int end_col1 = GetEndPeg().GetPosition().GetCol(); int start_row2 = link2.GetStartPeg().GetPosition().GetRow(); int start_col2 = link2.GetStartPeg().GetPosition().GetCol(); int end_row2 = link2.GetEndPeg().GetPosition().GetRow(); int end_col2 = link2.GetEndPeg().GetPosition().GetCol(); bool nearby = false; // if the first links start col is within the second links column // range then the first link is nearby the second link if (between_incl(start_col1, start_col2, end_col2)) { nearby = true; } // if the first links end col is within the second links column // range then the first link is nearby the second link if (between_incl(end_col1, start_col2, end_col2)) { nearby = true; } // if the first links start row is within the second links row // range then the first link is nearby the second link if (between_incl(start_row1, start_row2, end_row2)) { nearby = true; } // if the first links end row is within the second links row // range then the first link is nearby the second link if (between_incl(end_row1, start_row2, end_row2)) { nearby = true; } return nearby; } // checks to see if the links intersect // find intersection of extended links based on links // then determine if the intersection is within the link segment // retrun true if the links intersect, false otherwise bool Tw_Link::Intersect(const float slope1, const float interc1, const Tw_Link& link2, const float slope2, const float interc2) const { // find intersection of extended lines based on links // basically finding values for x and y such that the two lines // intersect - so we are solving simultaneous equations representing // the two lines for the one point in common // x is columns y is rows // y = (slope1 * x) + interc1 // y = (slope2 * x) + interc2 // substituting for y we get: // (slope2 * x) + interc2 = (slope1 * x) + interc1 // solving for x we get: // // (slope2 * x) - (slope1 * x) = interc1 - interc2 // // (slope2 - slope1) * x = (interc1 - interc2) // // x = (interc1 - interc2) // ------------------- // (slope2 - slope1) // // once x is calculated either of the equations can be used to // calculate y float intersect_col = (interc1 - interc2) / (slope2 - slope1); float intersect_row = interc2 + (slope2 * intersect_col); clog << "intersection between " << *this << " and " << link2 << " would be at (" << intersect_row << "," << intersect_col << ")" << endl; // now see if the intersection is within the link segments // here the row and column for the intersection must BOTH // be within the relevant ranges of BOTH links int start_row1 = GetStartPeg().GetPosition().GetRow(); int start_col1 = GetStartPeg().GetPosition().GetCol(); int end_row1 = GetEndPeg().GetPosition().GetRow(); int end_col1 = GetEndPeg().GetPosition().GetCol(); int start_row2 = link2.GetStartPeg().GetPosition().GetRow(); int start_col2 = link2.GetStartPeg().GetPosition().GetCol(); int end_row2 = link2.GetEndPeg().GetPosition().GetRow(); int end_col2 = link2.GetEndPeg().GetPosition().GetCol(); float float_start_row1 = static_cast(start_row1); float float_start_col1 = static_cast(start_col1); float float_end_row1 = static_cast(end_row1); float float_end_col1 = static_cast(end_col1); float float_start_row2 = static_cast(start_row2); float float_start_col2 = static_cast(start_col2); float float_end_row2 = static_cast(end_row2); float float_end_col2 = static_cast(end_col2); //DEBUG clog << "Checking static casts: " << float_start_row1 << " " << float_start_col1 << " " << float_end_row1 << " " << float_end_col1 << " " << float_start_row2 << " " << float_start_col2 << " " << float_end_row2 << " " << float_end_col2 << endl; // default to that they intersect and then change if necessary // (a simple way of implementing AND) bool intersect = true; // if the intersection col is not within the first links column // range then the intersection is not within the link // (i.e. the links do not block each other) if (!between(intersect_col, float_start_col1, float_end_col1)) { clog << "intersect column is not within column range of first link " << intersect_col << " " << start_col1 << " " << end_col1 << " - no chance of conflict " << endl; intersect = false; } // if the intersection col is not within the second links column // range then the intersection is not within the link // (i.e. the links do not block each other) if (!between(intersect_col, float_start_col2, float_end_col2)) { clog << "intersect column is not within column range of second link " << intersect_col << " " << start_col2 << " " << end_col2 << " - no chance of conflict " << endl; intersect = false; } // if the intersection row is not within the first links row // range then the intersection is not within the link // (i.e. the links do not block each other) if (!between(intersect_row, float_start_row1, float_end_row1)) { clog << "intersect row is not within row range of first link " << intersect_row << " " << start_row1 << " " << end_row1 << " - no chance of conflict " << endl; intersect = false; } // if the intersection row is not within the second links row // range then the intersection is not within the link // (i.e. the links do not block each other) if (!between(intersect_row, float_start_row2, float_end_row2)) { clog << "intersect row is not within row range of second link " << intersect_row << " " << start_row2 << " " << end_row2 << " - no chance of conflict " << endl; intersect = false; } return intersect; } // determine if link shares a common end point with another link bool Tw_Link::common_end_pt(const Tw_Link& link2) const { Tw_Peg startpeg1 = GetStartPeg(); Tw_Peg startpeg2 = link2.GetStartPeg(); Tw_Peg endpeg1 = GetEndPeg(); Tw_Peg endpeg2 = link2.GetEndPeg(); // if either end of one link matches either end of other then share common end point if ((startpeg1 == startpeg2) || (startpeg1 == endpeg2) || (endpeg1 == startpeg2) || (endpeg1 == endpeg2)) { return true; } else return false; } // determine if peg is part of a link bool Tw_Link::peg_part_of_link(const Tw_Peg& testpeg) const { Tw_Peg startpeg = GetStartPeg(); Tw_Peg endpeg = GetEndPeg(); // if either end of link matches the peg then the peg is part of the link if ((startpeg == testpeg) || (endpeg == testpeg)) { return true; } else return false; } // returns peg that is the opposite part of the link from the given // one Tw_Peg Tw_Link::other_part_of_link(const Tw_Peg& testpeg) const { Tw_Peg startpeg = GetStartPeg(); Tw_Peg endpeg = GetEndPeg(); // if either end of link matches the peg then the peg is part of the link if (startpeg == testpeg) { return endpeg; } if (endpeg == testpeg) { return startpeg; } // only done if neither of the above match // best to only call this function after successful call to above function // peg_part_of_link // if not part of link return the badpeg peg return Tw_Peg(badrow,badcol,badcolor); } // determines if two links are equal // order doesn't matter if the pegs are the same then the link is // the same // requires == to be defined for Tw_Peg bool Tw_Link::LinksEqual(const Tw_Link& link2) const { const Tw_Peg start1 = GetStartPeg(); const Tw_Peg start2 = link2.GetStartPeg(); const Tw_Peg end1 = GetEndPeg(); const Tw_Peg end2 = link2.GetEndPeg(); const Tw_Color col1 = GetLinkColor(); const Tw_Color col2 = link2.GetLinkColor(); // not equal if colors do not match if (col1 != col2) { return false; } // pegs match - links == if ((start1 == start2) && (end1 == end2)) { return true; } // just reversed - links == if ((start1 == end2) && (end1 == start2)) { return true; } // if it gets to here then we dont have a match return false; } // inserting a Tw_Link // uses iostream operator << - insertion into a stream // should receive as an argument an iostream object // commonly cout - standard output void Tw_Link::Insert(ostream &sout) const { sout << GetStartPeg() << ": " << GetLinkColor() << " :" << GetEndPeg(); } // returns a one char abreviation for the color // like insert but returns the char instead of putting it in // the output stream char Tw_Link::GetCharRep() const { char colchar; switch(GetLinkColor().GetColor()) { case empty: colchar = '.'; break; case black: colchar = 'b'; break; case white: colchar = 'w'; break; case illegal: colchar = 'i'; break; default: colchar = 'e'; } return colchar; } // extracting a Tw_Link // uses iostream operator >> - extract from a stream into object(s) // should receive as an argument an iostream object // commonly cin - standard input void Tw_Link::Extract(istream &sin) { char colon1; char colon2; Tw_Peg startpeg; Tw_Peg endpeg; Tw_Color color; // obtain two pegs and color from user - pulling apart into // its components // uses the fact that >> has been defined for pegs and colors sin >> startpeg >> colon2 >> color >> colon2 >> endpeg; // use mutator functions to update the data members // (the safe way - doesn't depend on implementation) SetStartPeg(startpeg); SetEndPeg(endpeg); SetLinkColor(color); return; } Tw_Peg Tw_Link::GetStartPeg() const { return startpeg; } Tw_Peg Tw_Link::GetEndPeg() const { return endpeg; } Tw_Color Tw_Link::GetLinkColor() const { return linkcolorv; } void Tw_Link::SetStartPeg(const Tw_Peg& peg) { startpeg = peg; } void Tw_Link::SetEndPeg(const Tw_Peg& peg) { endpeg = peg; } void Tw_Link::SetLinkColor(const Tw_Color& col) { linkcolorv = col; } // inserting a Tw_Link // overloading built-in << so can use same syntax as with built-in object types ostream& operator<<(ostream &sout, const Tw_Link &link) { // uses member function for the class to actually carry out // the operation link.Insert(sout); // return the result so that this insertion can be cascaded // with other insertions in a single expression return sout; } // extracting a Tw_Link // overloading built-in >> so can use same syntax as with built-in object types istream& operator>>(istream &sin, Tw_Link &link) { // uses member function for the class to actually carry out // the operation link.Extract(sin); // return the stream object so that this extraction can be cascaded // with other extractions in a single expression return sin; } bool operator==(const Tw_Link &left, const Tw_Link &right) { return left.LinksEqual(right); }