<commit_before>//! Values taken from RFC 2616

use extra::time::{Tm, strptime};

/*
/// OCTET: any 8-bit sequence of data (typechecking ensures this to be true)
#[inline]
pub fn is_octet(_: u8) -> bool { true }

/// CHAR: any US-ASCII character (octets 0 - 127)
#[inline]
pub fn is_char(octet: u8) -> bool { octet < 128 }

/// UPALPHA: any US-ASCII uppercase letter "A".."Z">
#[inline]
pub fn is_upalpha(octet: u8) -> bool { octet >= 65 && octet <= 90 }

/// LOALPHA: any US-ASCII lowercase letter "a".."z">
#[inline]
pub fn is_loalpha(octet: u8) -> bool { octet >= 97 && octet <= 122 }

/// ALPHA: UPALPHA | LOALPHA
#[inline]
pub fn is_alpha(octet: u8) -> bool { is_upalpha(octet) || is_loalpha(octet) }

/// DIGIT: any US-ASCII digit "0".."9"
#[inline]
pub fn is_digit(octet: u8) -> bool { octet >= 48 && octet <= 57 }

/// CTL: any US-ASCII control character (octets 0 - 31) and DEL (127)
#[inline]
pub fn is_ctl(octet: u8) -> bool { octet < 32 || octet == 127 }
*/

/// CR: US-ASCII CR, carriage return (13)
pub static CR: u8 = 13;

/// LF: US-ASCII LF, linefeed (10)
pub static LF: u8 = 10;

/// SP: US-ASCII SP, space (32)
pub static SP: u8 = 32;

/// HT: US-ASCII HT, horizontal-tab (9)
pub static HT: u8 = 9;

/// US-ASCII colon (58)
pub static COLON: u8 = 58;

/// <">: US-ASCII double-quote mark (34)
pub static DOUBLE_QUOTE: u8 = 34;

#[inline]
pub fn is_sp_or_ht(octet: u8) -> bool { octet == SP || octet == HT }

// CRLF: CR LF
//static CRLF: [u8] = [CR, LF];

// LWS: [CRLF] 1*( SP | HT )
/*#[inline]
fn is_lws(octets: &[u8]) -> bool {
    let mut has_crlf = false;
    let mut iter = octets.iter();
    if len(octets) == 0 {
        return false;
    }
    if len(octets) > 2 && octets[0] == CR && octets[1] == LF {
        iter = iter.next().next();
    }
    iter.all(|&octet| octet == SP || octet == HT)
}
*/

/*
   The TEXT rule is only used for descriptive field contents and values
   that are not intended to be interpreted by the message parser. Words
   of *TEXT MAY contain characters from character sets other than ISO-
   8859-1 [22] only when encoded according to the rules of RFC 2047
   [14].

       TEXT           = <any OCTET except CTLs,
                        but including LWS>

   A CRLF is allowed in the definition of TEXT only as part of a header
   field continuation. It is expected that the folding LWS will be
   replaced with a single SP before interpretation of the TEXT value.
*/

/*
/// HEX: "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
#[inline]
pub fn is_hex(octet: u8) -> bool {
    (octet >= 65 && octet <= 70) || (octet >= 97 && octet <= 102) || is_digit(octet)
}

/// token          = 1*<any CHAR except CTLs or separators>

/// separators: "(" | ")" | "<" | ">" | "@" | "," | ";" | ":"
///           | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{"
///           | "}" | SP | HT
#[inline]
pub fn is_separator(o: u8) -> bool {
    o == 40 || o == 41 || o == 60 || o == 62 || o == 64 || o == 44 || o == 59 || o == 58 || o == 92
        || o == 34 || o == 47 || o == 91 || o == 93 || o == 63 || o == 61 || o == 123 ||
        o == 125 || o == SP || o == HT
}
*/
/*
 * Comments can be included in some HTTP header fields by surrounding
 * the comment text with parentheses. Comments are only allowed in
 * fields containing "comment" as part of their field value definition.
 * In all other fields, parentheses are considered part of the field
 * value.
 *
 *     comment        = "(" *( ctext | quoted-pair | comment ) ")"
 *     ctext          = <any TEXT excluding "(" and ")">
 *
 * A string of text is parsed as a single word if it is quoted using
 * double-quote marks.
 *
 *     quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
 *     qdtext         = <any TEXT except <">>
 *
 * The backslash character ("\") MAY be used as a single-character
 * quoting mechanism only within quoted-string and comment constructs.
 *
 *     quoted-pair    = "\" CHAR
 */
//#[inline]
//fn is_quoted_pair(o: &[u8, ..2]) { o[0] == 92 }

/**
 * HTTP applications have historically allowed three different formats
 * for the representation of date/time stamps:
 *
 *    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
 *    Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
 *    Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
 *
 * The first format is preferred as an Internet standard and represents
 * a fixed-length subset of that defined by RFC 1123 [8] (an update to
 * RFC 822 [9]). The second format is in common use, but is based on the
 * obsolete RFC 850 [12] date format and lacks a four-digit year.
 * HTTP/1.1 clients and servers that parse the date value MUST accept
 * all three formats (for compatibility with HTTP/1.0), though they MUST
 * only generate the RFC 1123 format for representing HTTP-date values
 * in header fields. See section 19.3 for further information.
 *
 *    Note: Recipients of date values are encouraged to be robust in
 *    accepting date values that may have been sent by non-HTTP
 *    applications, as is sometimes the case when retrieving or posting
 *    messages via proxies/gateways to SMTP or NNTP.
 *
 * All HTTP date/time stamps MUST be represented in Greenwich Mean Time
 * (GMT), without exception. For the purposes of HTTP, GMT is exactly
 * equal to UTC (Coordinated Universal Time). This is indicated in the
 * first two formats by the inclusion of "GMT" as the three-letter
 * abbreviation for time zone, and MUST be assumed when reading the
 * asctime format. HTTP-date is case sensitive and MUST NOT include
 * additional LWS beyond that specifically included as SP in the
 * grammar.
 *
 *     HTTP-date    = rfc1123-date | rfc850-date | asctime-date
 *     rfc1123-date = wkday "," SP date1 SP time SP "GMT"
 *     rfc850-date  = weekday "," SP date2 SP time SP "GMT"
 *     asctime-date = wkday SP date3 SP time SP 4DIGIT
 *     date1        = 2DIGIT SP month SP 4DIGIT
 *                    ; day month year (e.g., 02 Jun 1982)
 *     date2        = 2DIGIT "-" month "-" 2DIGIT
 *                    ; day-month-year (e.g., 02-Jun-82)
 *     date3        = month SP ( 2DIGIT | ( SP 1DIGIT ))
 *                    ; month day (e.g., Jun  2)
 *     time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
 *                    ; 00:00:00 - 23:59:59
 *     wkday        = "Mon" | "Tue" | "Wed"
 *                  | "Thu" | "Fri" | "Sat" | "Sun"
 *     weekday      = "Monday" | "Tuesday" | "Wednesday"
 *                  | "Thursday" | "Friday" | "Saturday" | "Sunday"
 *     month        = "Jan" | "Feb" | "Mar" | "Apr"
 *                  | "May" | "Jun" | "Jul" | "Aug"
 *                  | "Sep" | "Oct" | "Nov" | "Dec"
 *
 *    Note: HTTP requirements for the date/time stamp format apply only
 *    to their usage within the protocol stream. Clients and servers are
 *    not required to use these formats for user presentation, request
 *    logging, etc.
 */
pub fn parse_http_time(value: &str) -> Option<Tm> {
    // XXX: %Z actually ignores any timezone other than UTC. Probably not a good idea?
    match strptime(value, "%a, %d %b %Y %T %Z") {  // RFC 822, updated by RFC 1123
        Ok(time) => return Some(time),
        Err(*) => ()
    }

    match strptime(value, "%A, %d-%b-%y %T %Z") {  // RFC 850, obsoleted by RFC 1036
        Ok(time) => return Some(time),
        Err(*) => ()
    }

    match strptime(value, "%c") {  // ANSI C's asctime() format
        Ok(time) => return Some(time),
        Err(*) => return None
    }
}

pub fn format_http_time(value: Tm) -> ~str {
    value.to_utc().strftime("%a, %d %b %Y %T GMT")
}

// IANA is assigned as maintaining the registry for these things:
// see https://www.iana.org/assignments/http-parameters/http-parameters.xml

/// Content-coding value tokens
pub enum ContentCodingValueToken {
    // An encoding format produced by the file compression program "gzip" (GNU zip) as described in
    // RFC 1952 [25]. This format is a Lempel-Ziv coding (LZ77) with a 32 bit CRC.
    CCVTGzip,

    // The encoding format produced by the common UNIX file compression program "compress". This
    // format is an adaptive Lempel-Ziv-Welch coding (LZW).
    //
    // Use of program names for the identification of encoding formats is not desirable and is
    // discouraged for future encodings. Their use here is representative of historical practice,
    // not good design. For compatibility with previous implementations of HTTP, applications SHOULD
    // consider "x-gzip" and "x-compress" to be equivalent to "gzip" and "compress" respectively.
    CCVTCompress,

    // The "zlib" format defined in RFC 1950 [31] in combination with the "deflate" compression
    // mechanism described in RFC 1951 [29].
    CCVTDeflate,

    // The default (identity) encoding; the use of no transformation whatsoever. This content-coding
    // is used only in the Accept- Encoding header, and SHOULD NOT be used in the Content-Encoding
    // header.
    CCVTIdentity,

    // IANA has also assigned the following currently unsupported content codings:
    //
    // - "exi": W3C Efficient XML Interchange
    // - "pack200-gzip" (Network Transfer Format for Java Archives)
}

/// Transfer-coding value tokens
// Identity is in RFC 2616 but is withdrawn in RFC 2616 errata ID 408
// http://www.rfc-editor.org/errata_search.php?rfc=2616&eid=408
pub enum TransferCodingValueToken {
    TCVTChunked,   // RFC 2616, §3.6.1
    TCVTGzip,      // See above
    TCVTCompress,  // See above
    TCVTDeflate,   // See above
}

// A server which receives an entity-body with a transfer-coding it does
// not understand SHOULD return 501 (Unimplemented), and close the
// connection. A server MUST NOT send transfer-codings to an HTTP/1.0
// client.

// [My note: implication of this is that I need to track HTTP version number of clients.]

#[cfg(test)]
mod test {
    use super::*;
    use extra::time::Tm;

    fn sample_tm(zone: ~str) -> Option<Tm> {
        Some(Tm {
            tm_sec: 37,
            tm_min: 49,
            tm_hour: 8,
            tm_mday: 6,
            tm_mon: 10,
            tm_year: 94,
            tm_wday: 0,
            tm_yday: 0,
            tm_isdst: 0,
            tm_gmtoff: 0,
            tm_zone: zone,
            tm_nsec: 0
        })
    }

    /// Test `parse_http_time` with an RFC 822 time (updated by RFC 1123)
    #[test]
    fn test_parse_http_time_rfc822() {
        assert_eq!(parse_http_time("Sun, 06 Nov 1994 08:49:37 GMT"), sample_tm(~"UTC"));
    }

    /// Test `parse_http_time` with an RFC 850 time (obsoleted by RFC 1036)
    #[test]
    fn test_parse_http_time_rfc850() {
        assert_eq!(parse_http_time("Sunday, 06-Nov-94 08:49:37 GMT"), sample_tm(~"UTC"));
    }

    /// Test `parse_http_time` with the ANSI C's asctime() format
    #[test]
    fn test_parse_http_time_asctime() {
        assert_eq!(parse_http_time("Sun Nov  6 08:49:37 1994"), sample_tm(~""));
    }
}
<commit_msg>Add a RFC 2616 token definition.<commit_after>//! Values taken from RFC 2616

use extra::time::{Tm, strptime};

/// OCTET: any 8-bit sequence of data (typechecking ensures this to be true)
#[inline]
pub fn is_octet(_: u8) -> bool { true }

/// CHAR: any US-ASCII character (octets 0 - 127)
#[inline]
pub fn is_char(octet: u8) -> bool { octet < 128 }

/// UPALPHA: any US-ASCII uppercase letter "A".."Z">
#[inline]
pub fn is_upalpha(octet: u8) -> bool { octet >= 65 && octet <= 90 }

/// LOALPHA: any US-ASCII lowercase letter "a".."z">
#[inline]
pub fn is_loalpha(octet: u8) -> bool { octet >= 97 && octet <= 122 }

/// ALPHA: UPALPHA | LOALPHA
#[inline]
pub fn is_alpha(octet: u8) -> bool { is_upalpha(octet) || is_loalpha(octet) }

/// DIGIT: any US-ASCII digit "0".."9"
#[inline]
pub fn is_digit(octet: u8) -> bool { octet >= 48 && octet <= 57 }

/// CTL: any US-ASCII control character (octets 0 - 31) and DEL (127)
#[inline]
pub fn is_ctl(octet: u8) -> bool { octet < 32 || octet == 127 }

/// CR: US-ASCII CR, carriage return (13)
pub static CR: u8 = 13;

/// LF: US-ASCII LF, linefeed (10)
pub static LF: u8 = 10;

/// SP: US-ASCII SP, space (32)
pub static SP: u8 = 32;

/// HT: US-ASCII HT, horizontal-tab (9)
pub static HT: u8 = 9;

/// US-ASCII colon (58)
pub static COLON: u8 = 58;

/// <">: US-ASCII double-quote mark (34)
pub static DOUBLE_QUOTE: u8 = 34;

#[inline]
pub fn is_sp_or_ht(octet: u8) -> bool { octet == SP || octet == HT }

// CRLF: CR LF
//static CRLF: [u8] = [CR, LF];

// LWS: [CRLF] 1*( SP | HT )
/*#[inline]
fn is_lws(octets: &[u8]) -> bool {
    let mut has_crlf = false;
    let mut iter = octets.iter();
    if len(octets) == 0 {
        return false;
    }
    if len(octets) > 2 && octets[0] == CR && octets[1] == LF {
        iter = iter.next().next();
    }
    iter.all(|&octet| octet == SP || octet == HT)
}
*/

/*
   The TEXT rule is only used for descriptive field contents and values
   that are not intended to be interpreted by the message parser. Words
   of *TEXT MAY contain characters from character sets other than ISO-
   8859-1 [22] only when encoded according to the rules of RFC 2047
   [14].

       TEXT           = <any OCTET except CTLs,
                        but including LWS>

   A CRLF is allowed in the definition of TEXT only as part of a header
   field continuation. It is expected that the folding LWS will be
   replaced with a single SP before interpretation of the TEXT value.
*/

/// HEX: "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
#[inline]
pub fn is_hex(octet: u8) -> bool {
    (octet >= 65 && octet <= 70) || (octet >= 97 && octet <= 102) || is_digit(octet)
}

/// token          = 1*<any CHAR except CTLs or separators>
#[inline]
pub fn is_token_item(o: u8) -> bool {
    is_char(o) && !is_ctl(o) && !is_separator(o)
}

/// separators: "(" | ")" | "<" | ">" | "@" | "," | ";" | ":"
///           | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{"
///           | "}" | SP | HT
#[inline]
pub fn is_separator(o: u8) -> bool {
    o == 40 || o == 41 || o == 60 || o == 62 || o == 64 || o == 44 || o == 59 || o == 58 || o == 92
        || o == 34 || o == 47 || o == 91 || o == 93 || o == 63 || o == 61 || o == 123 ||
        o == 125 || o == SP || o == HT
}

/*
 * Comments can be included in some HTTP header fields by surrounding
 * the comment text with parentheses. Comments are only allowed in
 * fields containing "comment" as part of their field value definition.
 * In all other fields, parentheses are considered part of the field
 * value.
 *
 *     comment        = "(" *( ctext | quoted-pair | comment ) ")"
 *     ctext          = <any TEXT excluding "(" and ")">
 *
 * A string of text is parsed as a single word if it is quoted using
 * double-quote marks.
 *
 *     quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
 *     qdtext         = <any TEXT except <">>
 *
 * The backslash character ("\") MAY be used as a single-character
 * quoting mechanism only within quoted-string and comment constructs.
 *
 *     quoted-pair    = "\" CHAR
 */
//#[inline]
//fn is_quoted_pair(o: &[u8, ..2]) { o[0] == 92 }

/**
 * HTTP applications have historically allowed three different formats
 * for the representation of date/time stamps:
 *
 *    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
 *    Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
 *    Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
 *
 * The first format is preferred as an Internet standard and represents
 * a fixed-length subset of that defined by RFC 1123 [8] (an update to
 * RFC 822 [9]). The second format is in common use, but is based on the
 * obsolete RFC 850 [12] date format and lacks a four-digit year.
 * HTTP/1.1 clients and servers that parse the date value MUST accept
 * all three formats (for compatibility with HTTP/1.0), though they MUST
 * only generate the RFC 1123 format for representing HTTP-date values
 * in header fields. See section 19.3 for further information.
 *
 *    Note: Recipients of date values are encouraged to be robust in
 *    accepting date values that may have been sent by non-HTTP
 *    applications, as is sometimes the case when retrieving or posting
 *    messages via proxies/gateways to SMTP or NNTP.
 *
 * All HTTP date/time stamps MUST be represented in Greenwich Mean Time
 * (GMT), without exception. For the purposes of HTTP, GMT is exactly
 * equal to UTC (Coordinated Universal Time). This is indicated in the
 * first two formats by the inclusion of "GMT" as the three-letter
 * abbreviation for time zone, and MUST be assumed when reading the
 * asctime format. HTTP-date is case sensitive and MUST NOT include
 * additional LWS beyond that specifically included as SP in the
 * grammar.
 *
 *     HTTP-date    = rfc1123-date | rfc850-date | asctime-date
 *     rfc1123-date = wkday "," SP date1 SP time SP "GMT"
 *     rfc850-date  = weekday "," SP date2 SP time SP "GMT"
 *     asctime-date = wkday SP date3 SP time SP 4DIGIT
 *     date1        = 2DIGIT SP month SP 4DIGIT
 *                    ; day month year (e.g., 02 Jun 1982)
 *     date2        = 2DIGIT "-" month "-" 2DIGIT
 *                    ; day-month-year (e.g., 02-Jun-82)
 *     date3        = month SP ( 2DIGIT | ( SP 1DIGIT ))
 *                    ; month day (e.g., Jun  2)
 *     time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
 *                    ; 00:00:00 - 23:59:59
 *     wkday        = "Mon" | "Tue" | "Wed"
 *                  | "Thu" | "Fri" | "Sat" | "Sun"
 *     weekday      = "Monday" | "Tuesday" | "Wednesday"
 *                  | "Thursday" | "Friday" | "Saturday" | "Sunday"
 *     month        = "Jan" | "Feb" | "Mar" | "Apr"
 *                  | "May" | "Jun" | "Jul" | "Aug"
 *                  | "Sep" | "Oct" | "Nov" | "Dec"
 *
 *    Note: HTTP requirements for the date/time stamp format apply only
 *    to their usage within the protocol stream. Clients and servers are
 *    not required to use these formats for user presentation, request
 *    logging, etc.
 */
pub fn parse_http_time(value: &str) -> Option<Tm> {
    // XXX: %Z actually ignores any timezone other than UTC. Probably not a good idea?
    match strptime(value, "%a, %d %b %Y %T %Z") {  // RFC 822, updated by RFC 1123
        Ok(time) => return Some(time),
        Err(*) => ()
    }

    match strptime(value, "%A, %d-%b-%y %T %Z") {  // RFC 850, obsoleted by RFC 1036
        Ok(time) => return Some(time),
        Err(*) => ()
    }

    match strptime(value, "%c") {  // ANSI C's asctime() format
        Ok(time) => return Some(time),
        Err(*) => return None
    }
}

pub fn format_http_time(value: Tm) -> ~str {
    value.to_utc().strftime("%a, %d %b %Y %T GMT")
}

// IANA is assigned as maintaining the registry for these things:
// see https://www.iana.org/assignments/http-parameters/http-parameters.xml

/// Content-coding value tokens
pub enum ContentCodingValueToken {
    // An encoding format produced by the file compression program "gzip" (GNU zip) as described in
    // RFC 1952 [25]. This format is a Lempel-Ziv coding (LZ77) with a 32 bit CRC.
    CCVTGzip,

    // The encoding format produced by the common UNIX file compression program "compress". This
    // format is an adaptive Lempel-Ziv-Welch coding (LZW).
    //
    // Use of program names for the identification of encoding formats is not desirable and is
    // discouraged for future encodings. Their use here is representative of historical practice,
    // not good design. For compatibility with previous implementations of HTTP, applications SHOULD
    // consider "x-gzip" and "x-compress" to be equivalent to "gzip" and "compress" respectively.
    CCVTCompress,

    // The "zlib" format defined in RFC 1950 [31] in combination with the "deflate" compression
    // mechanism described in RFC 1951 [29].
    CCVTDeflate,

    // The default (identity) encoding; the use of no transformation whatsoever. This content-coding
    // is used only in the Accept- Encoding header, and SHOULD NOT be used in the Content-Encoding
    // header.
    CCVTIdentity,

    // IANA has also assigned the following currently unsupported content codings:
    //
    // - "exi": W3C Efficient XML Interchange
    // - "pack200-gzip" (Network Transfer Format for Java Archives)
}

/// Transfer-coding value tokens
// Identity is in RFC 2616 but is withdrawn in RFC 2616 errata ID 408
// http://www.rfc-editor.org/errata_search.php?rfc=2616&eid=408
pub enum TransferCodingValueToken {
    TCVTChunked,   // RFC 2616, §3.6.1
    TCVTGzip,      // See above
    TCVTCompress,  // See above
    TCVTDeflate,   // See above
}

// A server which receives an entity-body with a transfer-coding it does
// not understand SHOULD return 501 (Unimplemented), and close the
// connection. A server MUST NOT send transfer-codings to an HTTP/1.0
// client.

// [My note: implication of this is that I need to track HTTP version number of clients.]

#[cfg(test)]
mod test {
    use super::*;
    use extra::time::Tm;

    fn sample_tm(zone: ~str) -> Option<Tm> {
        Some(Tm {
            tm_sec: 37,
            tm_min: 49,
            tm_hour: 8,
            tm_mday: 6,
            tm_mon: 10,
            tm_year: 94,
            tm_wday: 0,
            tm_yday: 0,
            tm_isdst: 0,
            tm_gmtoff: 0,
            tm_zone: zone,
            tm_nsec: 0
        })
    }

    /// Test `parse_http_time` with an RFC 822 time (updated by RFC 1123)
    #[test]
    fn test_parse_http_time_rfc822() {
        assert_eq!(parse_http_time("Sun, 06 Nov 1994 08:49:37 GMT"), sample_tm(~"UTC"));
    }

    /// Test `parse_http_time` with an RFC 850 time (obsoleted by RFC 1036)
    #[test]
    fn test_parse_http_time_rfc850() {
        assert_eq!(parse_http_time("Sunday, 06-Nov-94 08:49:37 GMT"), sample_tm(~"UTC"));
    }

    /// Test `parse_http_time` with the ANSI C's asctime() format
    #[test]
    fn test_parse_http_time_asctime() {
        assert_eq!(parse_http_time("Sun Nov  6 08:49:37 1994"), sample_tm(~""));
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeSet;
use action::{POSSIBLE_DIRECTIONS,POSSIBLE_MOVE_TYPES,Coordinate,Move};

pub const NUM_COLS: usize = 5;
pub const NUM_ROWS: usize = 4;

// Hacks for the three states of the lower-left of the board in JunSeok vs YeonSeung game
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub enum BoardType {
    Left,
    LeftOrMiddle,
    Middle,
    RightOrMiddle,
    Right,
}

impl BoardType {
    fn is_final(&self) -> bool {
        match *self {
            BoardType::Left => true,
            BoardType::Middle => true,
            BoardType::Right => true,
            BoardType::LeftOrMiddle => false,
            BoardType::RightOrMiddle => false,
        }
    }

    // Can a board of type `current` become this type?
    fn applies_to(&self, current: Option<BoardType>) -> bool {
        match (current, *self) {
            // a none board type can change to anything.
            (None, _) => true,
            // LeftOrMiddle can change to itself, left, or middle.
            (Some(BoardType::LeftOrMiddle), BoardType::LeftOrMiddle) => true,
            (Some(BoardType::LeftOrMiddle), BoardType::Left) => true,
            (Some(BoardType::LeftOrMiddle), BoardType::Middle) => true,
            (Some(BoardType::LeftOrMiddle), _) => false,
            // RightOrMiddle can change to itself, right, or middle.
            (Some(BoardType::RightOrMiddle), BoardType::RightOrMiddle) => true,
            (Some(BoardType::RightOrMiddle), BoardType::Right) => true,
            (Some(BoardType::RightOrMiddle), BoardType::Middle) => true,
            (Some(BoardType::RightOrMiddle), _) => false,
            // Left, Middle, Right can change to themselves only.
            (Some(BoardType::Left), BoardType::Left) => true,
            (Some(BoardType::Left), _) => false,
            (Some(BoardType::Middle), BoardType::Middle) => true,
            (Some(BoardType::Middle), _) => false,
            (Some(BoardType::Right), BoardType::Right) => true,
            (Some(BoardType::Right), _) => false,
        }
    }

    fn induced_by(&self, c: Coordinate) -> bool {
        // Not in the lower left, so it's a free pass.
        if !c.induces_board_type() {
            return true;
        }

        match *self {
            BoardType::Left          => c != Coordinate{row: 2, col: 1} && c != Coordinate{row: 1, col: 1},
            BoardType::LeftOrMiddle  => c == Coordinate{row: 1, col: 0},
            BoardType::Middle        => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 1, col: 1},
            BoardType::RightOrMiddle => c == Coordinate{row: 3, col: 1},
            BoardType::Right         => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 2, col: 0},
        }
    }
}

const POSSIBLE_BOARD_TYPES: [BoardType; 5] = [
    BoardType::Left,
    BoardType::LeftOrMiddle,
    BoardType::Middle,
    BoardType::RightOrMiddle,
    BoardType::Right,
];

pub type BoardArray = [[bool; NUM_COLS]; NUM_ROWS];
pub struct Board {
    board: BoardArray,
    board_type: Option<BoardType>,
}

impl Board {
    pub fn new(array: BoardArray, board_type: Option<BoardType>) -> Board {
        Board{board: array, board_type: board_type}
    }

    pub fn make_move(&mut self, m: Move) {
        if let Some(bt) = m.new_board_type {
            if !bt.applies_to(self.board_type) {
                panic!("Board type is {:?}, not compatible with {:?}", self.board_type, bt);
            }
            self.board_type = m.new_board_type
        }
        self.set_squares(m, true)
    }

    pub fn undo_move(&mut self, m: Move) {
        self.board_type = m.old_board_type;
        self.set_squares(m, false)
    }

    fn set_squares(&mut self, m: Move, mode: bool) {
        self.board[m.coord.row][m.coord.col] = mode;
        for other_space in m.extensions().iter() {
            self.board[other_space.row][other_space.col] = mode;
        }
    }

    fn occupied(&self, c: Coordinate) -> bool {
        self.board[c.row][c.col]
    }

    // This assesses whether a coordinate can be placed on the board,
    // given the current type of the board.
    fn compatible(&self, c: Coordinate) -> bool {
        // Not in the lower left, so it's a free pass.
        if !c.induces_board_type() {
            return true;
        }

        match self.board_type {
            Some(BoardType::Left)          => c != Coordinate{row: 2, col: 1} && c != Coordinate{row: 1, col: 1},
            Some(BoardType::LeftOrMiddle)  => c != Coordinate{row: 1, col: 1},
            Some(BoardType::Middle)        => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 1, col: 1},
            Some(BoardType::RightOrMiddle) => c != Coordinate{row: 3, col: 0},
            Some(BoardType::Right)         => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 2, col: 0},
            None => true,
        }
    }

    fn frontier(&self) -> Vec<Coordinate> {
        let mut results = Vec::new();
        for row in 0..NUM_ROWS {
            for col in 0..NUM_COLS {
                let coord = Coordinate{row: row, col: col};
                if self.occupied(coord) || !self.compatible(coord) {
                    continue;
                }
                let have_neighbor = POSSIBLE_DIRECTIONS.iter().any(|dir| {
                    if let Some(dest) = coord.move_in(*dir, 1) { self.occupied(dest) } else { false }
                });
                if have_neighbor {
                    results.push(coord);
                }
            }
        }
        results
    }

    fn board_type_final(&self) -> bool {
        if let Some(x) = self.board_type { x.is_final() } else { false }
    }

    pub fn legal_moves(&self) -> Vec<Move> {
        let mut results = Vec::new();
        for frontier_space in self.frontier().iter() {
            for move_type in POSSIBLE_MOVE_TYPES.iter() {
                let mov = match Move::new(*frontier_space, *move_type, self.board_type) {
                    Some(x) => x,
                    None => continue,
                };
                let mut other_space_taken = false;
                let mut induces_board_type = frontier_space.induces_board_type();
                for other_space in mov.extensions().iter() {
                    if other_space.induces_board_type() {
                        induces_board_type = true;
                    }
                    if self.occupied(*other_space) || !self.compatible(*other_space) {
                        other_space_taken = true;
                        break;
                    }
                }
                if other_space_taken {
                    continue;
                }

                if !induces_board_type || self.board_type_final() {
                    results.push(mov);
                    continue;
                }

                let mut ok_board_types = BTreeSet::new();
                for board_type in POSSIBLE_BOARD_TYPES.iter() {
                    if !board_type.applies_to(self.board_type) {
                        continue;
                    }
                    if !board_type.induced_by(*frontier_space) {
                        continue;
                    }
                    if mov.extensions().iter().all(|coord| board_type.induced_by(*coord)) {
                        ok_board_types.insert(*board_type);
                    }
                }

                // Dominated board types...
                if ok_board_types.contains(&BoardType::LeftOrMiddle) {
                    ok_board_types.remove(&BoardType::Left);
                    ok_board_types.remove(&BoardType::Middle);
                }
                if ok_board_types.contains(&BoardType::RightOrMiddle) {
                    ok_board_types.remove(&BoardType::Right);
                    ok_board_types.remove(&BoardType::Middle);
                }

                for board_type in ok_board_types.iter() {
                    results.push(mov.with_board_type(*board_type));
                }
            }
        }
        results
    }

    pub fn print(&self) {
        // Print header row
        print!("   ");
        for i in 0..NUM_COLS {
            print!("{: >5} ", i);
        }
        println!("");

        for (i, row) in self.board.iter().enumerate() {
            print!("{: >2} ", i);
            for col in row.iter() {
                print!("{: >5} ", col);
            }
            println!("");
        }
        println!("{:?}", self.board_type);
    }
}

<commit_msg>Board#legal_moves: Use filter for board types<commit_after>use std::collections::BTreeSet;
use action::{POSSIBLE_DIRECTIONS,POSSIBLE_MOVE_TYPES,Coordinate,Move};

pub const NUM_COLS: usize = 5;
pub const NUM_ROWS: usize = 4;

// Hacks for the three states of the lower-left of the board in JunSeok vs YeonSeung game
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub enum BoardType {
    Left,
    LeftOrMiddle,
    Middle,
    RightOrMiddle,
    Right,
}

impl BoardType {
    fn is_final(&self) -> bool {
        match *self {
            BoardType::Left => true,
            BoardType::Middle => true,
            BoardType::Right => true,
            BoardType::LeftOrMiddle => false,
            BoardType::RightOrMiddle => false,
        }
    }

    // Can a board of type `current` become this type?
    fn applies_to(&self, current: Option<BoardType>) -> bool {
        match (current, *self) {
            // a none board type can change to anything.
            (None, _) => true,
            // LeftOrMiddle can change to itself, left, or middle.
            (Some(BoardType::LeftOrMiddle), BoardType::LeftOrMiddle) => true,
            (Some(BoardType::LeftOrMiddle), BoardType::Left) => true,
            (Some(BoardType::LeftOrMiddle), BoardType::Middle) => true,
            (Some(BoardType::LeftOrMiddle), _) => false,
            // RightOrMiddle can change to itself, right, or middle.
            (Some(BoardType::RightOrMiddle), BoardType::RightOrMiddle) => true,
            (Some(BoardType::RightOrMiddle), BoardType::Right) => true,
            (Some(BoardType::RightOrMiddle), BoardType::Middle) => true,
            (Some(BoardType::RightOrMiddle), _) => false,
            // Left, Middle, Right can change to themselves only.
            (Some(BoardType::Left), BoardType::Left) => true,
            (Some(BoardType::Left), _) => false,
            (Some(BoardType::Middle), BoardType::Middle) => true,
            (Some(BoardType::Middle), _) => false,
            (Some(BoardType::Right), BoardType::Right) => true,
            (Some(BoardType::Right), _) => false,
        }
    }

    fn induced_by(&self, c: Coordinate) -> bool {
        // Not in the lower left, so it's a free pass.
        if !c.induces_board_type() {
            return true;
        }

        match *self {
            BoardType::Left          => c != Coordinate{row: 2, col: 1} && c != Coordinate{row: 1, col: 1},
            BoardType::LeftOrMiddle  => c == Coordinate{row: 1, col: 0},
            BoardType::Middle        => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 1, col: 1},
            BoardType::RightOrMiddle => c == Coordinate{row: 3, col: 1},
            BoardType::Right         => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 2, col: 0},
        }
    }
}

const POSSIBLE_BOARD_TYPES: [BoardType; 5] = [
    BoardType::Left,
    BoardType::LeftOrMiddle,
    BoardType::Middle,
    BoardType::RightOrMiddle,
    BoardType::Right,
];

pub type BoardArray = [[bool; NUM_COLS]; NUM_ROWS];
pub struct Board {
    board: BoardArray,
    board_type: Option<BoardType>,
}

impl Board {
    pub fn new(array: BoardArray, board_type: Option<BoardType>) -> Board {
        Board{board: array, board_type: board_type}
    }

    pub fn make_move(&mut self, m: Move) {
        if let Some(bt) = m.new_board_type {
            if !bt.applies_to(self.board_type) {
                panic!("Board type is {:?}, not compatible with {:?}", self.board_type, bt);
            }
            self.board_type = m.new_board_type
        }
        self.set_squares(m, true)
    }

    pub fn undo_move(&mut self, m: Move) {
        self.board_type = m.old_board_type;
        self.set_squares(m, false)
    }

    fn set_squares(&mut self, m: Move, mode: bool) {
        self.board[m.coord.row][m.coord.col] = mode;
        for other_space in m.extensions().iter() {
            self.board[other_space.row][other_space.col] = mode;
        }
    }

    fn occupied(&self, c: Coordinate) -> bool {
        self.board[c.row][c.col]
    }

    // This assesses whether a coordinate can be placed on the board,
    // given the current type of the board.
    fn compatible(&self, c: Coordinate) -> bool {
        // Not in the lower left, so it's a free pass.
        if !c.induces_board_type() {
            return true;
        }

        match self.board_type {
            Some(BoardType::Left)          => c != Coordinate{row: 2, col: 1} && c != Coordinate{row: 1, col: 1},
            Some(BoardType::LeftOrMiddle)  => c != Coordinate{row: 1, col: 1},
            Some(BoardType::Middle)        => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 1, col: 1},
            Some(BoardType::RightOrMiddle) => c != Coordinate{row: 3, col: 0},
            Some(BoardType::Right)         => c != Coordinate{row: 3, col: 0} && c != Coordinate{row: 2, col: 0},
            None => true,
        }
    }

    fn frontier(&self) -> Vec<Coordinate> {
        let mut results = Vec::new();
        for row in 0..NUM_ROWS {
            for col in 0..NUM_COLS {
                let coord = Coordinate{row: row, col: col};
                if self.occupied(coord) || !self.compatible(coord) {
                    continue;
                }
                let have_neighbor = POSSIBLE_DIRECTIONS.iter().any(|dir| {
                    if let Some(dest) = coord.move_in(*dir, 1) { self.occupied(dest) } else { false }
                });
                if have_neighbor {
                    results.push(coord);
                }
            }
        }
        results
    }

    fn board_type_final(&self) -> bool {
        if let Some(x) = self.board_type { x.is_final() } else { false }
    }

    pub fn legal_moves(&self) -> Vec<Move> {
        let mut results = Vec::new();
        for frontier_space in self.frontier().iter() {
            for move_type in POSSIBLE_MOVE_TYPES.iter() {
                let mov = match Move::new(*frontier_space, *move_type, self.board_type) {
                    Some(x) => x,
                    None => continue,
                };
                let mut other_space_taken = false;
                let mut induces_board_type = frontier_space.induces_board_type();
                for other_space in mov.extensions().iter() {
                    if other_space.induces_board_type() {
                        induces_board_type = true;
                    }
                    if self.occupied(*other_space) || !self.compatible(*other_space) {
                        other_space_taken = true;
                        break;
                    }
                }
                if other_space_taken {
                    continue;
                }

                if !induces_board_type || self.board_type_final() {
                    results.push(mov);
                    continue;
                }

                let mut ok_board_types: BTreeSet<_> = POSSIBLE_BOARD_TYPES.iter().cloned().filter(|board_type| {
                    board_type.applies_to(self.board_type) &&
                        board_type.induced_by(*frontier_space) &&
                        mov.extensions().iter().all(|coord| board_type.induced_by(*coord))
                }).collect();

                // Dominated board types...
                if ok_board_types.contains(&BoardType::LeftOrMiddle) {
                    ok_board_types.remove(&BoardType::Left);
                    ok_board_types.remove(&BoardType::Middle);
                }
                if ok_board_types.contains(&BoardType::RightOrMiddle) {
                    ok_board_types.remove(&BoardType::Right);
                    ok_board_types.remove(&BoardType::Middle);
                }

                for board_type in ok_board_types.iter() {
                    results.push(mov.with_board_type(*board_type));
                }
            }
        }
        results
    }

    pub fn print(&self) {
        // Print header row
        print!("   ");
        for i in 0..NUM_COLS {
            print!("{: >5} ", i);
        }
        println!("");

        for (i, row) in self.board.iter().enumerate() {
            print!("{: >2} ", i);
            for col in row.iter() {
                print!("{: >5} ", col);
            }
            println!("");
        }
        println!("{:?}", self.board_type);
    }
}

<|endoftext|>


<commit_before>use crate::config::*;
use crate::errors::RunError;
use cargo_metadata::{diagnostic::DiagnosticLevel, CargoOpt, Message, MetadataCommand};
use log::{error, trace};
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use walkdir::WalkDir;

static DOCTEST_FOLDER: &str = "target/doctests";

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TestBinary {
    path: PathBuf,
    ty: RunType,
    cargo_dir: Option<PathBuf>,
    pkg_name: Option<String>,
    pkg_version: Option<String>,
    pkg_authors: Option<Vec<String>>,
}

impl TestBinary {
    pub fn new(path: PathBuf, ty: RunType) -> Self {
        Self {
            path,
            ty,
            pkg_name: None,
            pkg_version: None,
            pkg_authors: None,
            cargo_dir: None,
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn run_type(&self) -> RunType {
        self.ty
    }

    pub fn manifest_dir(&self) -> &Option<PathBuf> {
        &self.cargo_dir
    }

    pub fn pkg_name(&self) -> &Option<String> {
        &self.pkg_name
    }

    pub fn pkg_version(&self) -> &Option<String> {
        &self.pkg_version
    }

    pub fn pkg_authors(&self) -> &Option<Vec<String>> {
        &self.pkg_authors
    }
}

pub fn get_tests(config: &Config) -> Result<Vec<TestBinary>, RunError> {
    let mut result = vec![];
    let manifest = match config.manifest.as_path().to_str() {
        Some(s) => s,
        None => "Cargo.toml",
    };
    let metadata = MetadataCommand::new()
        .manifest_path(manifest)
        .features(CargoOpt::AllFeatures)
        .exec()
        .map_err(|e| RunError::Cargo(e.to_string()))?;

    for ty in &config.run_types {
        let mut cmd = create_command(manifest, config, ty);
        cmd.stdout(Stdio::piped());
        if !config.verbose {
            cmd.stderr(Stdio::null());
        }
        trace!("Running command {:?}", cmd);
        let mut child = cmd.spawn().map_err(|e| RunError::Cargo(e.to_string()))?;

        if ty != &RunType::Doctests {
            let mut package_ids = vec![];
            let reader = std::io::BufReader::new(child.stdout.take().unwrap());
            for msg in Message::parse_stream(reader) {
                match msg {
                    Ok(Message::CompilerArtifact(art)) => {
                        if let Some(path) = art.executable {
                            if !art.profile.test && ty == &RunType::Tests {
                                continue;
                            }
                            result.push(TestBinary::new(path, *ty));
                            package_ids.push(art.package_id.clone());
                        }
                    }
                    Ok(Message::CompilerMessage(m)) => match m.message.level {
                        DiagnosticLevel::Error | DiagnosticLevel::Ice => {
                            let _ = child.wait();
                            return Err(RunError::TestCompile(m.message.message));
                        }
                        _ => {}
                    },
                    Err(e) => {
                        error!("Error parsing cargo messages {}", e);
                    }
                    _ => {}
                }
            }
            for (res, package) in result.iter_mut().zip(package_ids.iter()) {
                let package = &metadata[package];
                res.cargo_dir = package.manifest_path.parent().map(|x| x.to_path_buf());
                res.pkg_name = Some(package.name.clone());
                res.pkg_version = Some(package.version.to_string());
                res.pkg_authors = Some(package.authors.clone());
            }
        } else {
            // Need to get the packages...
            let package_roots = config
                .get_packages()
                .iter()
                .filter_map(|x| x.manifest_path.parent())
                .map(|x| x.join(DOCTEST_FOLDER))
                .collect::<Vec<PathBuf>>();

            for dir in &package_roots {
                let walker = WalkDir::new(dir).into_iter();
                for dt in walker
                    .filter_map(|e| e.ok())
                    .filter(|e| match e.metadata() {
                        Ok(ref m) if m.is_file() && m.len() != 0 => true,
                        _ => false,
                    })
                {
                    result.push(TestBinary::new(dt.path().to_path_buf(), *ty));
                }
            }
        }
        child.wait().map_err(|e| RunError::Cargo(e.to_string()))?;
    }
    Ok(result)
}

fn create_command(manifest_path: &str, config: &Config, ty: &RunType) -> Command {
    let mut test_cmd = Command::new("cargo");
    if *ty == RunType::Doctests {
        if let Some(toolchain) = env::var("RUSTUP_TOOLCHAIN")
            .ok()
            .filter(|t| t.starts_with("nightly"))
        {
            test_cmd.args(&[format!("+{}", toolchain).as_str(), "test"]);
        } else {
            test_cmd.args(&["+nightly", "test"]);
        }
    } else {
        if let Ok(toolchain) = env::var("RUSTUP_TOOLCHAIN") {
            test_cmd.arg(format!("+{}", toolchain));
        }
        test_cmd.args(&["test", "--no-run"]);
    }
    test_cmd.args(&["--message-format", "json", "--manifest-path", manifest_path]);
    match ty {
        RunType::Tests => test_cmd.arg("--tests"),
        RunType::Doctests => test_cmd.arg("--doc"),
        RunType::Benchmarks => test_cmd.arg("--benches"),
        RunType::Examples => test_cmd.arg("--examples"),
    };
    init_args(&mut test_cmd, config);
    setup_environment(&mut test_cmd, config);
    test_cmd
}

fn init_args(test_cmd: &mut Command, config: &Config) {
    if config.debug {
        test_cmd.arg("-vvv");
    }
    if config.locked {
        test_cmd.arg("--locked");
    }
    if config.frozen {
        test_cmd.arg("--frozen");
    }
    if let Some(features) = config.features.as_ref() {
        test_cmd.arg("--features");
        test_cmd.arg(features);
    }
    if config.all_features {
        test_cmd.arg("--all-features");
    }
    if config.no_default_features {
        test_cmd.arg("--no-default-features");
    }
    if config.all {
        test_cmd.arg("--workspace");
    }
    if config.release {
        test_cmd.arg("--release");
    }
    if let Some(target) = config.target.as_ref() {
        test_cmd.args(&["--target", target]);
    }
    let args = vec![
        "--target-dir".to_string(),
        format!("{}", config.target_dir().display()),
    ];
    test_cmd.args(args);
    if config.offline {
        test_cmd.arg("--offline");
    }
    for feat in &config.unstable_features {
        test_cmd.arg(format!("-Z{}", feat));
    }
    if !config.varargs.is_empty() {
        let mut args = vec!["--".to_string()];
        args.extend_from_slice(&config.varargs);
        test_cmd.args(args);
    }
}

fn setup_environment(cmd: &mut Command, config: &Config) {
    cmd.env("TARPAULIN", "1");
    let rustflags = "RUSTFLAGS";
    let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C debuginfo=2 ";
    let mut value = common_opts.to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    cmd.env(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!(
        "{} --persist-doctests {} -Z unstable-options ",
        common_opts, DOCTEST_FOLDER
    );
    if let Ok(vtemp) = env::var(rustdoc) {
        if !vtemp.contains("--persist-doctests") {
            value.push_str(vtemp.as_ref());
        }
    }
    cmd.env(rustdoc, value);
}
<commit_msg>Passes package IDs through to cargo.<commit_after>use crate::config::*;
use crate::errors::RunError;
use cargo_metadata::{diagnostic::DiagnosticLevel, CargoOpt, Message, MetadataCommand};
use log::{error, trace};
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use walkdir::WalkDir;

static DOCTEST_FOLDER: &str = "target/doctests";

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TestBinary {
    path: PathBuf,
    ty: RunType,
    cargo_dir: Option<PathBuf>,
    pkg_name: Option<String>,
    pkg_version: Option<String>,
    pkg_authors: Option<Vec<String>>,
}

impl TestBinary {
    pub fn new(path: PathBuf, ty: RunType) -> Self {
        Self {
            path,
            ty,
            pkg_name: None,
            pkg_version: None,
            pkg_authors: None,
            cargo_dir: None,
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn run_type(&self) -> RunType {
        self.ty
    }

    pub fn manifest_dir(&self) -> &Option<PathBuf> {
        &self.cargo_dir
    }

    pub fn pkg_name(&self) -> &Option<String> {
        &self.pkg_name
    }

    pub fn pkg_version(&self) -> &Option<String> {
        &self.pkg_version
    }

    pub fn pkg_authors(&self) -> &Option<Vec<String>> {
        &self.pkg_authors
    }
}

pub fn get_tests(config: &Config) -> Result<Vec<TestBinary>, RunError> {
    let mut result = vec![];
    let manifest = match config.manifest.as_path().to_str() {
        Some(s) => s,
        None => "Cargo.toml",
    };
    let metadata = MetadataCommand::new()
        .manifest_path(manifest)
        .features(CargoOpt::AllFeatures)
        .exec()
        .map_err(|e| RunError::Cargo(e.to_string()))?;

    for ty in &config.run_types {
        let mut cmd = create_command(manifest, config, ty);
        cmd.stdout(Stdio::piped());
        if !config.verbose {
            cmd.stderr(Stdio::null());
        }
        trace!("Running command {:?}", cmd);
        let mut child = cmd.spawn().map_err(|e| RunError::Cargo(e.to_string()))?;

        if ty != &RunType::Doctests {
            let mut package_ids = vec![];
            let reader = std::io::BufReader::new(child.stdout.take().unwrap());
            for msg in Message::parse_stream(reader) {
                match msg {
                    Ok(Message::CompilerArtifact(art)) => {
                        if let Some(path) = art.executable {
                            if !art.profile.test && ty == &RunType::Tests {
                                continue;
                            }
                            result.push(TestBinary::new(path, *ty));
                            package_ids.push(art.package_id.clone());
                        }
                    }
                    Ok(Message::CompilerMessage(m)) => match m.message.level {
                        DiagnosticLevel::Error | DiagnosticLevel::Ice => {
                            let _ = child.wait();
                            return Err(RunError::TestCompile(m.message.message));
                        }
                        _ => {}
                    },
                    Err(e) => {
                        error!("Error parsing cargo messages {}", e);
                    }
                    _ => {}
                }
            }
            for (res, package) in result.iter_mut().zip(package_ids.iter()) {
                let package = &metadata[package];
                res.cargo_dir = package.manifest_path.parent().map(|x| x.to_path_buf());
                res.pkg_name = Some(package.name.clone());
                res.pkg_version = Some(package.version.to_string());
                res.pkg_authors = Some(package.authors.clone());
            }
        } else {
            // Need to get the packages...
            let package_roots = config
                .get_packages()
                .iter()
                .filter_map(|x| x.manifest_path.parent())
                .map(|x| x.join(DOCTEST_FOLDER))
                .collect::<Vec<PathBuf>>();

            for dir in &package_roots {
                let walker = WalkDir::new(dir).into_iter();
                for dt in walker
                    .filter_map(|e| e.ok())
                    .filter(|e| match e.metadata() {
                        Ok(ref m) if m.is_file() && m.len() != 0 => true,
                        _ => false,
                    })
                {
                    result.push(TestBinary::new(dt.path().to_path_buf(), *ty));
                }
            }
        }
        child.wait().map_err(|e| RunError::Cargo(e.to_string()))?;
    }
    Ok(result)
}

fn create_command(manifest_path: &str, config: &Config, ty: &RunType) -> Command {
    let mut test_cmd = Command::new("cargo");
    if *ty == RunType::Doctests {
        if let Some(toolchain) = env::var("RUSTUP_TOOLCHAIN")
            .ok()
            .filter(|t| t.starts_with("nightly"))
        {
            test_cmd.args(&[format!("+{}", toolchain).as_str(), "test"]);
        } else {
            test_cmd.args(&["+nightly", "test"]);
        }
    } else {
        if let Ok(toolchain) = env::var("RUSTUP_TOOLCHAIN") {
            test_cmd.arg(format!("+{}", toolchain));
        }
        test_cmd.args(&["test", "--no-run"]);
    }
    test_cmd.args(&["--message-format", "json", "--manifest-path", manifest_path]);
    match ty {
        RunType::Tests => test_cmd.arg("--tests"),
        RunType::Doctests => test_cmd.arg("--doc"),
        RunType::Benchmarks => test_cmd.arg("--benches"),
        RunType::Examples => test_cmd.arg("--examples"),
    };
    init_args(&mut test_cmd, config);
    setup_environment(&mut test_cmd, config);
    test_cmd
}

fn init_args(test_cmd: &mut Command, config: &Config) {
    if config.debug {
        test_cmd.arg("-vvv");
    }
    if config.locked {
        test_cmd.arg("--locked");
    }
    if config.frozen {
        test_cmd.arg("--frozen");
    }
    if let Some(features) = config.features.as_ref() {
        test_cmd.arg("--features");
        test_cmd.arg(features);
    }
    if config.all_features {
        test_cmd.arg("--all-features");
    }
    if config.no_default_features {
        test_cmd.arg("--no-default-features");
    }
    if config.all {
        test_cmd.arg("--workspace");
    }
    if config.release {
        test_cmd.arg("--release");
    }
    config.packages.iter().for_each(|package| {
        test_cmd.arg("--package");
        test_cmd.arg(package);
    });
    if let Some(target) = config.target.as_ref() {
        test_cmd.args(&["--target", target]);
    }
    let args = vec![
        "--target-dir".to_string(),
        format!("{}", config.target_dir().display()),
    ];
    test_cmd.args(args);
    if config.offline {
        test_cmd.arg("--offline");
    }
    for feat in &config.unstable_features {
        test_cmd.arg(format!("-Z{}", feat));
    }
    if !config.varargs.is_empty() {
        let mut args = vec!["--".to_string()];
        args.extend_from_slice(&config.varargs);
        test_cmd.args(args);
    }
}

fn setup_environment(cmd: &mut Command, config: &Config) {
    cmd.env("TARPAULIN", "1");
    let rustflags = "RUSTFLAGS";
    let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C debuginfo=2 ";
    let mut value = common_opts.to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    cmd.env(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!(
        "{} --persist-doctests {} -Z unstable-options ",
        common_opts, DOCTEST_FOLDER
    );
    if let Ok(vtemp) = env::var(rustdoc) {
        if !vtemp.contains("--persist-doctests") {
            value.push_str(vtemp.as_ref());
        }
    }
    cmd.env(rustdoc, value);
}
<|endoftext|>


<commit_before>extern crate regex;
use self::regex::Regex;

#[derive(PartialEq, Debug)]
pub enum Piece {
    Rook,
    Knight,
    Bishop,
    Queen,
    King,
    Pawn,
}

impl Piece {
    fn to_char(&self) -> char {
        match *self {
            Piece::Rook   => 'R',
            Piece::Knight => 'N',
            Piece::Bishop => 'B',
            Piece::Queen  => 'Q',
            Piece::King   => 'K',
            Piece::Pawn   => 'P',
        }
    }

    fn from(c: char) -> Option<Piece> {
        match c {
            'R' => Some(Piece::Rook),
            'N' => Some(Piece::Knight),
            'B' => Some(Piece::Bishop),
            'Q' => Some(Piece::Queen),
            'K' => Some(Piece::King),
            'P' => Some(Piece::Pawn),
            _   => None,
        }
    }
}

#[test]
fn test_piece_to_str() {
    assert_eq!('R', Piece::Rook.to_char());
}

#[test]
fn test_piece_from_str() {
    assert_eq!(Some(Piece::Knight), Piece::from('N'));
}

////////////////////////////////////////////////////////////////////////////////

#[derive(PartialEq, Debug)]
pub enum Castle {
    Queenside,
    Kingside,
}

impl Castle {
    fn to_str(&self) -> String {
        match *self {
            Castle::Queenside => String::from("O-O-O"),
            Castle::Kingside  => String::from("O-O"),
        }
    }

    fn from_str(s: String) -> Option<Castle> {
        match s.as_ref() {
            "O-O"   => Some(Castle::Kingside),
            "O-O-O" => Some(Castle::Queenside),
            _       => None
        }
    }
}

#[test]
fn test_castle_to_str() {
    assert_eq!(Castle::Queenside.to_str(), "O-O-O");
}

#[test]
fn test_castle_from_str() {
    assert_eq!(Some(Castle::Queenside),
               Castle::from_str(String::from("O-O-O")));
}

////////////////////////////////////////////////////////////////////////////////

#[derive(PartialEq, Debug)]
struct Move {
    piece: Option<Piece>,
    from_file: Option<char>, // column, 'a' to 'h'
    from_rank: Option<i8>,   // row,     1  to  8
    capture: bool,
    to_file: Option<char>,   // column, 'a' to 'h'
    to_rank: Option<i8>,     // row,     1  to  8
    check: bool,
    checkmate: bool,
    promotion: Option<Piece>,
    castle: Option<Castle>,
}

impl Move {
    fn new() -> Move {
        Move {
            piece: None,
            from_file: None,
            from_rank: None,
            capture: false,
            to_file: None,
            to_rank: None,
            promotion: None,
            castle: None,
            check: false,
            checkmate: false,
        }
    }

    pub fn from(mov_str: &String) -> Move {
        // WOW, I should really write a regex for this
        let mut mov = Move::new();
        if mov_str.contains("x") {
            mov.capture = true;
        }
        if mov_str.contains("+") {
            mov.check = true;
        }
        if mov_str.contains("#") {
            mov.checkmate = true;
        }
        // Piece
        if mov_str.starts_with("R") {
            mov.piece = Some(Piece::Rook);
        } else if mov_str.starts_with("N") {
            mov.piece = Some(Piece::Knight);
        } else if mov_str.starts_with("B") {
            mov.piece = Some(Piece::Bishop);
        } else if mov_str.starts_with("Q") {
            mov.piece = Some(Piece::Queen);
        } else if mov_str.starts_with("K") {
            mov.piece = Some(Piece::King);
        } else if mov_str.contains("O-O-O") {
            // no piece
            mov.castle = Some(Castle::Queenside);
        } else if mov_str.contains("O-O") {
            // no piece
            mov.castle = Some(Castle::Kingside);
        } else {
            mov.piece = Some(Piece::Pawn);
        }
        if mov_str.contains("=") {
            // Promotion
            mov.promotion = Piece::from(mov_str.chars().nth(
                mov_str.find("=").unwrap() + 1).unwrap());
        }
        // Get from/to positions if there
        let move_re = Regex::new(r"(?P<piece>[RNBQK])?(?P<from_file>[abcdefgh])?(?P<from_rank>[12345678])?(?P<capture>x)?(?P<to_file>[abcdefgh])(?P<to_rank>[12345678])(?P<promotion>=[RNBQ])?(?P<check>\+)?(?P<checkmate>\#)?").unwrap();
        let move_caps = move_re.captures(mov_str);
        // In future, we could use the following regex for castle moves
        // let castle_re = Regex::new(r"O\-O(?P<qs>\-O)(?P<check>\+)?(?P<checkmate>\#)").unwrap();
        // let castle_caps = castle_re.captures(mov_str);
        if move_caps.is_some() {
            let c = move_caps.unwrap();
            if c.name("from_file").is_some() {
                mov.from_file =
                    c.name("from_file").unwrap().chars().nth(0);
            }
            if c.name("from_rank").is_some() {
                mov.from_rank =
                    Some(c.name("from_rank").unwrap().chars().nth(0).unwrap()
                         .to_digit(10).unwrap() as i8);
            }
            if c.name("to_file").is_some() {
                mov.to_file =
                    c.name("to_file").unwrap().chars().nth(0);
            }
            if c.name("to_rank").is_some() {
                mov.to_rank =
                    Some(c.name("to_rank").unwrap().chars().nth(0).unwrap()
                         .to_digit(10).unwrap() as i8);
            }
        }
        mov
    }

    pub fn to_str(&self) -> String {
        let mut ret = String::new();
        match self.piece {
            Some(Piece::Pawn) => (),
            Some(ref p) => ret.push(p.to_char()),
            _ => (),
        }
        if self.from_file.is_some() {
            ret.push(self.from_file.unwrap());
        }
        if self.from_rank.is_some() {
            ret.push(self.from_rank.unwrap().to_string().pop().unwrap());
        }
        if self.capture {
            ret.push('x');
        }
        if self.to_file.is_some() {
            ret.push(self.to_file.unwrap());
        }
        if self.to_rank.is_some() {
            ret.push(self.to_rank.unwrap().to_string().pop().unwrap());
        }
        match self.promotion {
            Some(Piece::Pawn) => (),
            Some(ref p) => {
                ret.push('=');
                ret.push(p.to_char());
            },
            _ => (),
        }
        match self.castle {
            Some(Castle::Kingside)  => ret.push_str("O-O"),
            Some(Castle::Queenside) => ret.push_str("O-O-O"),
            _ => (),
        }
        if self.check {
            ret.push('+');
        }
        if self.checkmate {
            ret.push('#');
        }
        ret
    }
}

#[test]
fn test_castle_move_from() {
    let mut m = Move::new();
    m.castle = Some(Castle::Kingside);
    assert_eq!(Move::from(&String::from("O-O")), m);
    m.checkmate = true;
    assert_eq!(Move::from(&String::from("O-O#")), m);
    m = Move::new();
    m.castle = Some(Castle::Queenside);
    m.check = true;
    assert_eq!(Move::from(&String::from("O-O-O+")), m);
}

#[test]
fn test_promotion_move_from() {
    let m = Move::from(&String::from("e8=Q"));
    let mut m2 = Move::new();
    m2.piece = Some(Piece::Pawn);
    m2.to_file = Some('e');
    m2.to_rank = Some(8 as i8);
    m2.promotion = Some(Piece::Queen);
    assert_eq!(m2, m);
}

#[test]
fn test_pawn_move_to_str() {
    let mut m = Move::new();
    m.to_file = Some('e');
    m.to_rank = Some(4 as i8);
    assert_eq!(m.to_str(), "e4");
    m.piece = Some(Piece::Pawn);
    assert_eq!(m.to_str(), "e4");
}

#[test]
fn test_longest_move_to_str() {
    // https://www.chess.com/blog/kurtgodden/think-you-know-algebraic-notation
    // according to the above site, the longest legal move is exd6+ e.p.,
    // but since PGN doesn't have special notation for en passant, I came up
    // with exd8=Q+
    let mut m = Move::new();
    m.piece = Some(Piece::Pawn);
    m.from_file = Some('e');
    m.capture = true;
    m.to_file = Some('d');
    m.to_rank = Some(8 as i8);
    m.promotion = Some(Piece::Queen);
    m.check = true;
    assert_eq!(m.to_str(), "exd8=Q+");
}

#[test]
fn test_move_to_str_and_from_str() {
    let mut m = Move::new();
    m.piece = Some(Piece::Pawn);
    m.from_file = Some('e');
    m.capture = true;
    m.to_file = Some('d');
    m.to_rank = Some(8 as i8);
    m.promotion = Some(Piece::Queen);
    m.check = true;
    assert_eq!(Move::from(&m.to_str()), m);
}

////////////////////////////////////////////////////////////////////////////////

pub enum Player {
    White,
    Black,
}

struct State {
    board: [[char; 8]; 8]
}

impl State {
    pub fn new() -> State {
        State {
            //        a    b    c    d    e    f    g    h
            board: [['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'], // 8
                    ['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'], // 7
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 6
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 5
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 4
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 3
                    ['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'], // 2
                    ['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']],// 1
        }
    }

    fn find_piece_location(&self, player: Player, mov: Move)
                           -> Option<(char, i8)> {
        // TODO
        None
    }

    pub fn make_move(&self, player: Player, mov: Move) -> Option<State> {
        // TODO
        None
    }
}
<commit_msg>More board state impl<commit_after>extern crate regex;
use self::regex::Regex;

#[derive(PartialEq, Debug)]
pub enum Piece {
    Rook,
    Knight,
    Bishop,
    Queen,
    King,
    Pawn,
}

impl Piece {
    fn to_char(&self) -> char {
        match *self {
            Piece::Rook   => 'R',
            Piece::Knight => 'N',
            Piece::Bishop => 'B',
            Piece::Queen  => 'Q',
            Piece::King   => 'K',
            Piece::Pawn   => 'P',
        }
    }

    fn from(c: char) -> Option<Piece> {
        match c {
            'R' => Some(Piece::Rook),
            'N' => Some(Piece::Knight),
            'B' => Some(Piece::Bishop),
            'Q' => Some(Piece::Queen),
            'K' => Some(Piece::King),
            'P' => Some(Piece::Pawn),
            _   => None,
        }
    }
}

#[test]
fn test_piece_to_str() {
    assert_eq!('R', Piece::Rook.to_char());
}

#[test]
fn test_piece_from_str() {
    assert_eq!(Some(Piece::Knight), Piece::from('N'));
}

////////////////////////////////////////////////////////////////////////////////

#[derive(PartialEq, Debug)]
pub enum Castle {
    Queenside,
    Kingside,
}

impl Castle {
    fn to_str(&self) -> String {
        match *self {
            Castle::Queenside => String::from("O-O-O"),
            Castle::Kingside  => String::from("O-O"),
        }
    }

    fn from_str(s: String) -> Option<Castle> {
        match s.as_ref() {
            "O-O"   => Some(Castle::Kingside),
            "O-O-O" => Some(Castle::Queenside),
            _       => None
        }
    }
}

#[test]
fn test_castle_to_str() {
    assert_eq!(Castle::Queenside.to_str(), "O-O-O");
}

#[test]
fn test_castle_from_str() {
    assert_eq!(Some(Castle::Queenside),
               Castle::from_str(String::from("O-O-O")));
}

////////////////////////////////////////////////////////////////////////////////

#[derive(PartialEq, Debug)]
struct Move {
    piece: Option<Piece>,
    from_file: Option<char>, // column, 'a' to 'h'
    from_rank: Option<i8>,   // row,     1  to  8
    capture: bool,
    to_file: Option<char>,   // column, 'a' to 'h'
    to_rank: Option<i8>,     // row,     1  to  8
    check: bool,
    checkmate: bool,
    promotion: Option<Piece>,
    castle: Option<Castle>,
}

impl Move {
    fn new() -> Move {
        Move {
            piece: None,
            from_file: None,
            from_rank: None,
            capture: false,
            to_file: None,
            to_rank: None,
            promotion: None,
            castle: None,
            check: false,
            checkmate: false,
        }
    }

    pub fn from(mov_str: &String) -> Move {
        // WOW, I should really write a regex for this
        let mut mov = Move::new();
        if mov_str.contains("x") {
            mov.capture = true;
        }
        if mov_str.contains("+") {
            mov.check = true;
        }
        if mov_str.contains("#") {
            mov.checkmate = true;
        }
        // Piece
        if mov_str.starts_with("R") {
            mov.piece = Some(Piece::Rook);
        } else if mov_str.starts_with("N") {
            mov.piece = Some(Piece::Knight);
        } else if mov_str.starts_with("B") {
            mov.piece = Some(Piece::Bishop);
        } else if mov_str.starts_with("Q") {
            mov.piece = Some(Piece::Queen);
        } else if mov_str.starts_with("K") {
            mov.piece = Some(Piece::King);
        } else if mov_str.contains("O-O-O") {
            // no piece
            mov.castle = Some(Castle::Queenside);
        } else if mov_str.contains("O-O") {
            // no piece
            mov.castle = Some(Castle::Kingside);
        } else {
            mov.piece = Some(Piece::Pawn);
        }
        if mov_str.contains("=") {
            // Promotion
            mov.promotion = Piece::from(mov_str.chars().nth(
                mov_str.find("=").unwrap() + 1).unwrap());
        }
        // Get from/to positions if there
        let move_re = Regex::new(r"(?P<piece>[RNBQK])?(?P<from_file>[abcdefgh])?(?P<from_rank>[12345678])?(?P<capture>x)?(?P<to_file>[abcdefgh])(?P<to_rank>[12345678])(?P<promotion>=[RNBQ])?(?P<check>\+)?(?P<checkmate>\#)?").unwrap();
        let move_caps = move_re.captures(mov_str);
        // In future, we could use the following regex for castle moves
        // let castle_re = Regex::new(r"O\-O(?P<qs>\-O)(?P<check>\+)?(?P<checkmate>\#)").unwrap();
        // let castle_caps = castle_re.captures(mov_str);
        if move_caps.is_some() {
            let c = move_caps.unwrap();
            if c.name("from_file").is_some() {
                mov.from_file =
                    c.name("from_file").unwrap().chars().nth(0);
            }
            if c.name("from_rank").is_some() {
                mov.from_rank =
                    Some(c.name("from_rank").unwrap().chars().nth(0).unwrap()
                         .to_digit(10).unwrap() as i8);
            }
            if c.name("to_file").is_some() {
                mov.to_file =
                    c.name("to_file").unwrap().chars().nth(0);
            }
            if c.name("to_rank").is_some() {
                mov.to_rank =
                    Some(c.name("to_rank").unwrap().chars().nth(0).unwrap()
                         .to_digit(10).unwrap() as i8);
            }
        }
        mov
    }

    pub fn to_str(&self) -> String {
        let mut ret = String::new();
        match self.piece {
            Some(Piece::Pawn) => (),
            Some(ref p) => ret.push(p.to_char()),
            _ => (),
        }
        if self.from_file.is_some() {
            ret.push(self.from_file.unwrap());
        }
        if self.from_rank.is_some() {
            ret.push(self.from_rank.unwrap().to_string().pop().unwrap());
        }
        if self.capture {
            ret.push('x');
        }
        if self.to_file.is_some() {
            ret.push(self.to_file.unwrap());
        }
        if self.to_rank.is_some() {
            ret.push(self.to_rank.unwrap().to_string().pop().unwrap());
        }
        match self.promotion {
            Some(Piece::Pawn) => (),
            Some(ref p) => {
                ret.push('=');
                ret.push(p.to_char());
            },
            _ => (),
        }
        match self.castle {
            Some(Castle::Kingside)  => ret.push_str("O-O"),
            Some(Castle::Queenside) => ret.push_str("O-O-O"),
            _ => (),
        }
        if self.check {
            ret.push('+');
        }
        if self.checkmate {
            ret.push('#');
        }
        ret
    }
}

#[test]
fn test_castle_move_from() {
    let mut m = Move::new();
    m.castle = Some(Castle::Kingside);
    assert_eq!(Move::from(&String::from("O-O")), m);
    m.checkmate = true;
    assert_eq!(Move::from(&String::from("O-O#")), m);
    m = Move::new();
    m.castle = Some(Castle::Queenside);
    m.check = true;
    assert_eq!(Move::from(&String::from("O-O-O+")), m);
}

#[test]
fn test_promotion_move_from() {
    let m = Move::from(&String::from("e8=Q"));
    let mut m2 = Move::new();
    m2.piece = Some(Piece::Pawn);
    m2.to_file = Some('e');
    m2.to_rank = Some(8 as i8);
    m2.promotion = Some(Piece::Queen);
    assert_eq!(m2, m);
}

#[test]
fn test_pawn_move_to_str() {
    let mut m = Move::new();
    m.to_file = Some('e');
    m.to_rank = Some(4 as i8);
    assert_eq!(m.to_str(), "e4");
    m.piece = Some(Piece::Pawn);
    assert_eq!(m.to_str(), "e4");
}

#[test]
fn test_longest_move_to_str() {
    // https://www.chess.com/blog/kurtgodden/think-you-know-algebraic-notation
    // according to the above site, the longest legal move is exd6+ e.p.,
    // but since PGN doesn't have special notation for en passant, I came up
    // with exd8=Q+
    let mut m = Move::new();
    m.piece = Some(Piece::Pawn);
    m.from_file = Some('e');
    m.capture = true;
    m.to_file = Some('d');
    m.to_rank = Some(8 as i8);
    m.promotion = Some(Piece::Queen);
    m.check = true;
    assert_eq!(m.to_str(), "exd8=Q+");
}

#[test]
fn test_move_to_str_and_from_str() {
    let mut m = Move::new();
    m.piece = Some(Piece::Pawn);
    m.from_file = Some('e');
    m.capture = true;
    m.to_file = Some('d');
    m.to_rank = Some(8 as i8);
    m.promotion = Some(Piece::Queen);
    m.check = true;
    assert_eq!(Move::from(&m.to_str()), m);
}

////////////////////////////////////////////////////////////////////////////////

#[derive(PartialEq, Debug)]
struct Posn {
    file: char, // 'a' to 'h'
    rank: i8,   //  1  to  8
}

impl Posn {
    fn from(s: &String) -> Posn {
        Posn {
            file: s.chars().nth(0).unwrap(),
            rank: s.chars().nth(1).unwrap().to_digit(10).unwrap() as i8,
        }
    }
}

#[test]
fn test_posn_from() {
    assert_eq!(Posn { file: 'e', rank: 4 as i8 },
               Posn::from(&String::from("e4")));
}

pub enum Player {
    White,
    Black,
}

#[derive(PartialEq, Debug)]
struct State {
    board: [[char; 8]; 8]
}

impl State {
    pub fn new() -> State {
        State {
            //        a    b    c    d    e    f    g    h
            board: [['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'], // 8
                    ['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'], // 7
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 6
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 5
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 4
                    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 3
                    ['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'], // 2
                    ['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']],// 1
        }
    }

    fn col_from_file(f: &char) -> Option<usize> {
        match f {
            &'a' => Some(0 as usize),
            &'b' => Some(1 as usize),
            &'c' => Some(2 as usize),
            &'d' => Some(3 as usize),
            &'e' => Some(4 as usize),
            &'f' => Some(5 as usize),
            &'g' => Some(6 as usize),
            &'h' => Some(7 as usize),
             _  => None,
        }
    }

    fn row_from_rank(r: &i8) -> Option<usize> {
        match r {
            &1 => Some(7 as usize),
            &2 => Some(6 as usize),
            &3 => Some(5 as usize),
            &4 => Some(4 as usize),
            &5 => Some(3 as usize),
            &6 => Some(2 as usize),
            &7 => Some(1 as usize),
            &8 => Some(0 as usize),
            _ => None,
        }
    }

    fn find_all_pieces(&self, player: Player, piece: Piece) -> Vec<Posn> {
        let v = Vec::new();
        v
    }

    fn find_piece_location(&self, player: Player, mov: Move) -> Option<Posn> {
        // TODO
        None
    }

    pub fn make_move(&self, player: Player, mov: Move) -> Option<State> {
        let to = Posn { file: mov.to_file.unwrap(),
                        rank: mov.to_rank.unwrap(), };
        match self.find_piece_location(player, mov) {
            Some(from) => Some(self.move_piece(from, to)),
            _ => None,
        }
    }

    fn move_piece(&self, from: Posn, to: Posn) -> State {
        let mut clone = self.clone();
        clone.board[State::row_from_rank(&to.rank).unwrap()]
            [State::col_from_file(&to.file).unwrap()] =
        clone.board[State::row_from_rank(&from.rank).unwrap()]
            [State::col_from_file(&from.file).unwrap()];
        clone.board[State::row_from_rank(&from.rank).unwrap()]
            [State::col_from_file(&from.file).unwrap()] = ' ';
        clone
    }
}

#[test]
fn test_col_from_file() {
    assert_eq!(Some(4 as usize), State::col_from_file(&'e'));
}

#[test]
fn test_row_from_rank() {
    assert_eq!(Some(4 as usize), State::row_from_rank(&(4 as i8)));
}

#[test]
fn test_move_piece() {
    assert_eq!(State::new().move_piece(Posn::from(&String::from("e2")),
                                       Posn::from(&String::from("e4"))),
               State {
                   //        a    b    c    d    e    f    g    h
                   board: [['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'], // 8
                           ['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'], // 7
                           [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 6
                           [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 5
                           [' ', ' ', ' ', ' ', '♙', ' ', ' ', ' '], // 4
                           [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], // 3
                           ['♙', '♙', '♙', '♙', ' ', '♙', '♙', '♙'], // 2
                           ['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']],// 1
               });
}

impl Clone for State {
    fn clone(&self) -> State {
        State { board: self.board.clone(), }
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

extern crate itertools;
use self::itertools::Itertools;

const DATA: &'static str = include_str!("../data/input_19.txt");

pub fn main() -> Vec<String> {
    let ctx = Context::from_str(DATA).unwrap();
    let s1 = ctx.all_replacements().count().to_string();
    let s2 = ctx.find_production(&ctx.medicine, 0).unwrap().to_string();
    vec![s1, s2]
}

#[derive(Debug)]
struct Context {
    replacements: Vec<(String, String)>,
    medicine: String,
}

impl FromStr for Context {
    type Err = ();

    fn from_str(data: &str) -> Result<Context, ()> {
        let mut input = data.lines();
        let mut replacements = Vec::new();

        while let Some(r) = input.next() {
            if r.trim().is_empty() {
                break;
            }
            let mut tokens = r.split(" => ");
            replacements.push((tokens.next().unwrap().to_owned(), tokens.next().unwrap().to_owned()));
        }
        let medicine = input.next().unwrap();
        assert_eq!(input.next(), None);

        // for part 2, we want to work backwards trying longest first
        // as we reduce the medicine molecule towards "e"
        // part 1 does not care about ordering of the replacement rules
        replacements.sort_by(|&(_, ref a), &(_, ref b)| b.len().cmp(&a.len()));

        Ok(Context {
            replacements: replacements,
            medicine: medicine.to_owned(),
        })
    }
}

impl<'a> Context {
    fn all_replacements(&'a self) -> Box<Iterator<Item = String> + 'a> {
        box self.replacements
                .iter()
                .flat_map(move |&(ref from, ref to)| {
                    (&self.medicine[..]).single_replacements(from, to).into_iter()
                })
                .unique()
    }

    fn find_production(&self, input: &str, depth: usize) -> Option<usize> {
        if input == "e" {
            return Some(depth);
        }
        for next_step in self.replacements
                             .iter()
                             .flat_map(|&(ref from, ref to)| {
                                 input.single_replacements(to, from).into_iter()
                             })
                             .unique() {
            if let Some(count) = self.find_production(&next_step, depth + 1) {
                return Some(count);
            }
        }
        None
    }
}

trait SingleReplacements {
    // like std::str::replace but returning a Vec<String> with one replacement in each
    fn single_replacements(&self, from: &str, to: &str) -> Vec<String>;
}

impl<'a> SingleReplacements for &'a str {
    fn single_replacements(&self, from: &str, to: &str) -> Vec<String> {
        let mut results = Vec::new();
        for (start, part) in self.match_indices(from) {
            let mut string = String::new();
            string.push_str(unsafe { self.slice_unchecked(0, start) });
            string.push_str(to);
            string.push_str(unsafe { self.slice_unchecked(start + part.len(), self.len()) });
            results.push(string);
        }
        results
    }
}
<commit_msg>day 19: catch errors in Context::frm_str<commit_after>use std::str::FromStr;

extern crate itertools;
use self::itertools::Itertools;

const DATA: &'static str = include_str!("../data/input_19.txt");

pub fn main() -> Vec<String> {
    let ctx = Context::from_str(DATA).unwrap();
    let s1 = ctx.all_replacements().count().to_string();
    let s2 = ctx.find_production(&ctx.medicine, 0).unwrap().to_string();
    vec![s1, s2]
}

#[derive(Debug)]
struct Context {
    replacements: Vec<(String, String)>,
    medicine: String,
}

impl FromStr for Context {
    type Err = ();

    fn from_str(data: &str) -> Result<Context, ()> {
        let mut input = data.lines();
        let mut replacements = Vec::new();

        while let Some(r) = input.next() {
            if r.trim().is_empty() {
                break;
            }
            let mut tokens = r.split(" => ");
            if let Some((l, r)) = tokens.next().and_then(|l| tokens.next().map(|r| (l, r))) {
                replacements.push((l.to_owned(), r.to_owned()))
            } else {
                return Err(());
            }
        }
        if let Some(medicine) = input.next() {
            // for part 2, we want to work backwards trying longest first
            // as we reduce the medicine molecule towards "e"
            // part 1 does not care about ordering of the replacement rules
            replacements.sort_by(|&(_, ref a), &(_, ref b)| b.len().cmp(&a.len()));

            Ok(Context {
                replacements: replacements,
                medicine: medicine.to_owned(),
            })
        } else {
            Err(())
        }
    }
}

impl<'a> Context {
    fn all_replacements(&'a self) -> Box<Iterator<Item = String> + 'a> {
        box self.replacements
                .iter()
                .flat_map(move |&(ref from, ref to)| {
                    (&self.medicine[..]).single_replacements(from, to).into_iter()
                })
                .unique()
    }

    fn find_production(&self, input: &str, depth: usize) -> Option<usize> {
        if input == "e" {
            return Some(depth);
        }
        for next_step in self.replacements
                             .iter()
                             .flat_map(|&(ref from, ref to)| {
                                 input.single_replacements(to, from).into_iter()
                             })
                             .unique() {
            if let Some(count) = self.find_production(&next_step, depth + 1) {
                return Some(count);
            }
        }
        None
    }
}

trait SingleReplacements {
    // like std::str::replace but returning a Vec<String> with one replacement in each
    fn single_replacements(&self, from: &str, to: &str) -> Vec<String>;
}

impl<'a> SingleReplacements for &'a str {
    fn single_replacements(&self, from: &str, to: &str) -> Vec<String> {
        let mut results = Vec::new();
        for (start, part) in self.match_indices(from) {
            let mut string = String::new();
            string.push_str(unsafe { self.slice_unchecked(0, start) });
            string.push_str(to);
            string.push_str(unsafe { self.slice_unchecked(start + part.len(), self.len()) });
            results.push(string);
        }
        results
    }
}
<|endoftext|>


<commit_before>/// Utilities for writing messages to the Janus log.
extern crate chrono;
extern crate colored;

pub use super::ffi::janus_log_level as JANUS_LOG_LEVEL;
use self::chrono::{DateTime, Local};
use self::colored::{Color, Colorize};
use super::ffi;
use std::ffi::CString;
use std::fmt::Write;
use std::fmt;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
/// A Janus log level. Lower is more severe.
pub enum LogLevel {
    Fatal = 1,
    Err = 2,
    Warn = 3,
    Info = 4,
    Verb = 5,
    Huge = 6,
    Dbg = 7,
}

impl LogLevel {
    /// The color associated with each log level's label (if colors are enabled.)
    fn color(&self) -> Option<Color> {
        match *self {
            LogLevel::Fatal => Some(Color::Magenta),
            LogLevel::Err => Some(Color::Red),
            LogLevel::Warn => Some(Color::Yellow),
            _ => None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct LogParameters {
    pub log_timestamps: bool,
    pub log_colors: bool,
    pub clock: fn() -> DateTime<Local>,
}

impl Default for LogParameters {
    fn default() -> Self {
        unsafe {
            Self {
                log_timestamps: ffi::janus_log_timestamps == 1,
                log_colors: ffi::janus_log_colors == 1,
                clock: Local::now,
            }
        }
    }
}

/// Writes a message at the given log level to the Janus log, using the provided parameters to control
/// how the log message is formatted.
pub fn log(level: LogLevel, message: fmt::Arguments, params: LogParameters) {
    unsafe {
        let output = CString::new(print_log(level, message, params));
        ffi::janus_vprintf(output.expect("Null character in log message :(").as_ptr())
    }
}

/// Prints a message at the given log level into an owned string, using the provided parameters to control
/// how the log message is formatted.
pub fn print_log(level: LogLevel, message: fmt::Arguments, params: LogParameters) -> String {
    let mut output = String::with_capacity(150); // reasonably conservative size for typical messages
    if params.log_timestamps {
        write!(output, "{} ", (params.clock)().format("[%a %b %e %T %Y]")).unwrap();
    }
    if level <= LogLevel::Warn {
        let prefix = format!("[{:?}] ", level).to_uppercase();
        let prefix = match level.color() {
            Some(c) if params.log_colors => format!("{}", prefix.color(c)),
            _ => prefix,
        };
        write!(output, "{}", prefix).unwrap();
    }
    output.write_fmt(message).expect("Error constructing log message!");
    output.push('\n');
    output
}

#[macro_export]
macro_rules! janus_log_enabled {
    ($lvl:expr) => (($lvl as i32) < unsafe { $crate::debug::JANUS_LOG_LEVEL })
}

#[macro_export]
macro_rules! janus_log {
    ($lvl:expr, $($arg:tt)+) => ({
        let lvl = $lvl;
        if janus_log_enabled!(lvl) {
            $crate::debug::log(lvl, format_args!($($arg)+), $crate::debug::LogParameters::default())
        }
    })
}

#[macro_export]
macro_rules! janus_fatal {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Fatal, $($arg)+))
}

#[macro_export]
macro_rules! janus_err {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Err, $($arg)+))
}

#[macro_export]
macro_rules! janus_warn {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Warn, $($arg)+))
}

#[macro_export]
macro_rules! janus_info {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Info, $($arg)+))
}

#[macro_export]
macro_rules! janus_verb {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Verb, $($arg)+))
}

#[macro_export]
macro_rules! janus_huge {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Huge, $($arg)+))
}

#[macro_export]
macro_rules! janus_dbg {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Dbg, $($arg)+))
}

#[cfg(test)]
mod tests {

    use super::*;
    use super::chrono::TimeZone;

    fn fixed_clock(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Local> {
        Local.ymd(year, month, day).and_hms(hour, min, sec)
    }

    #[test]
    fn log_format_correctness() {
        assert_eq!(
            "[Tue Oct 10 01:37:46 2017] [WARN] Test message.\n",
            print_log(
                LogLevel::Warn,
                format_args!("{}", "Test message."),
                LogParameters {
                    log_timestamps: true,
                    ..default_log_parameters()
                }
            )
        )
    }

    #[test]
    fn log_colored_output() {
        assert_eq!(
            "\u{1b}[35m[FATAL] \u{1b}[0mCrash!\n",
            print_log(
                LogLevel::Fatal,
                format_args!("{}", "Crash!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "\u{1b}[31m[ERR] \u{1b}[0mAn error occurred!\n",
            print_log(
                LogLevel::Err,
                format_args!("{}", "An error occurred!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "\u{1b}[33m[WARN] \u{1b}[0mAttention!\n",
            print_log(
                LogLevel::Warn,
                format_args!("{}", "Attention!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "Just a message.\n",
            print_log(
                LogLevel::Info,
                format_args!("{}", "Just a message."),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );
    }

    fn default_log_parameters() -> LogParameters {
        LogParameters {
            log_timestamps: false,
            log_colors: false,
            clock: || fixed_clock(2017, 10, 10, 1, 37, 46),
        }
    }
}
<commit_msg>Log messages with the same level as configured<commit_after>/// Utilities for writing messages to the Janus log.
extern crate chrono;
extern crate colored;

pub use super::ffi::janus_log_level as JANUS_LOG_LEVEL;
use self::chrono::{DateTime, Local};
use self::colored::{Color, Colorize};
use super::ffi;
use std::ffi::CString;
use std::fmt::Write;
use std::fmt;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
/// A Janus log level. Lower is more severe.
pub enum LogLevel {
    Fatal = 1,
    Err = 2,
    Warn = 3,
    Info = 4,
    Verb = 5,
    Huge = 6,
    Dbg = 7,
}

impl LogLevel {
    /// The color associated with each log level's label (if colors are enabled.)
    fn color(&self) -> Option<Color> {
        match *self {
            LogLevel::Fatal => Some(Color::Magenta),
            LogLevel::Err => Some(Color::Red),
            LogLevel::Warn => Some(Color::Yellow),
            _ => None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct LogParameters {
    pub log_timestamps: bool,
    pub log_colors: bool,
    pub clock: fn() -> DateTime<Local>,
}

impl Default for LogParameters {
    fn default() -> Self {
        unsafe {
            Self {
                log_timestamps: ffi::janus_log_timestamps == 1,
                log_colors: ffi::janus_log_colors == 1,
                clock: Local::now,
            }
        }
    }
}

/// Writes a message at the given log level to the Janus log, using the provided parameters to control
/// how the log message is formatted.
pub fn log(level: LogLevel, message: fmt::Arguments, params: LogParameters) {
    unsafe {
        let output = CString::new(print_log(level, message, params));
        ffi::janus_vprintf(output.expect("Null character in log message :(").as_ptr())
    }
}

/// Prints a message at the given log level into an owned string, using the provided parameters to control
/// how the log message is formatted.
pub fn print_log(level: LogLevel, message: fmt::Arguments, params: LogParameters) -> String {
    let mut output = String::with_capacity(150); // reasonably conservative size for typical messages
    if params.log_timestamps {
        write!(output, "{} ", (params.clock)().format("[%a %b %e %T %Y]")).unwrap();
    }
    if level <= LogLevel::Warn {
        let prefix = format!("[{:?}] ", level).to_uppercase();
        let prefix = match level.color() {
            Some(c) if params.log_colors => format!("{}", prefix.color(c)),
            _ => prefix,
        };
        write!(output, "{}", prefix).unwrap();
    }
    output.write_fmt(message).expect("Error constructing log message!");
    output.push('\n');
    output
}

#[macro_export]
macro_rules! janus_log_enabled {
    ($lvl:expr) => (($lvl as i32) <= unsafe { $crate::debug::JANUS_LOG_LEVEL })
}

#[macro_export]
macro_rules! janus_log {
    ($lvl:expr, $($arg:tt)+) => ({
        let lvl = $lvl;
        if janus_log_enabled!(lvl) {
            $crate::debug::log(lvl, format_args!($($arg)+), $crate::debug::LogParameters::default())
        }
    })
}

#[macro_export]
macro_rules! janus_fatal {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Fatal, $($arg)+))
}

#[macro_export]
macro_rules! janus_err {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Err, $($arg)+))
}

#[macro_export]
macro_rules! janus_warn {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Warn, $($arg)+))
}

#[macro_export]
macro_rules! janus_info {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Info, $($arg)+))
}

#[macro_export]
macro_rules! janus_verb {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Verb, $($arg)+))
}

#[macro_export]
macro_rules! janus_huge {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Huge, $($arg)+))
}

#[macro_export]
macro_rules! janus_dbg {
    ($($arg:tt)+) => (janus_log!($crate::debug::LogLevel::Dbg, $($arg)+))
}

#[cfg(test)]
mod tests {

    use super::*;
    use super::chrono::TimeZone;

    fn fixed_clock(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Local> {
        Local.ymd(year, month, day).and_hms(hour, min, sec)
    }

    #[test]
    fn log_format_correctness() {
        assert_eq!(
            "[Tue Oct 10 01:37:46 2017] [WARN] Test message.\n",
            print_log(
                LogLevel::Warn,
                format_args!("{}", "Test message."),
                LogParameters {
                    log_timestamps: true,
                    ..default_log_parameters()
                }
            )
        )
    }

    #[test]
    fn log_colored_output() {
        assert_eq!(
            "\u{1b}[35m[FATAL] \u{1b}[0mCrash!\n",
            print_log(
                LogLevel::Fatal,
                format_args!("{}", "Crash!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "\u{1b}[31m[ERR] \u{1b}[0mAn error occurred!\n",
            print_log(
                LogLevel::Err,
                format_args!("{}", "An error occurred!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "\u{1b}[33m[WARN] \u{1b}[0mAttention!\n",
            print_log(
                LogLevel::Warn,
                format_args!("{}", "Attention!"),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );

        assert_eq!(
            "Just a message.\n",
            print_log(
                LogLevel::Info,
                format_args!("{}", "Just a message."),
                LogParameters {
                    log_colors: true,
                    ..default_log_parameters()
                }
            )
        );
    }

    fn default_log_parameters() -> LogParameters {
        LogParameters {
            log_timestamps: false,
            log_colors: false,
            clock: || fixed_clock(2017, 10, 10, 1, 37, 46),
        }
    }
}
<|endoftext|>