2016-08-09 8 views
1

値を生成して別の生成値の要素として反映させるにはどうすればよいですか?値を生成して別の生成値の要素として反映させる方法はありますか?

たとえば、次のコードを取る:

type Space = 
    | Occupied of Piece 
    | Available of Coordinate 

// Setup 
let pieceGen =  Arb.generate<Piece> 
let destinationGen = Arb.generate<Space> 
let positionsGen = Arb.generate<Space list> 

私はpositionsGenがpieceGenとspaceGenによって生成された値を含めたいです。 しかし、私はこれをどうやって行うのか分かりません。

私の質問にコンテキストを追加するには、私のポジションリスト(別名チェッカーボード)に、生成されたピースと生成された宛先の両方をそのリストに含める必要があります。

[<Property(QuietOnSuccess = true, MaxTest=10000)>] 
let ``moving checker retains set count``() = 

    // Setup 
    let pieceGen =  Arb.generate<Piece> 
    let destinationGen = Arb.generate<Space> 
    let positionsGen = Arb.generate<Space list> 
    let statusGen =  Arb.generate<Status> 

    // Test 
    Gen.map4 (fun a b c d -> a,b,c,d) pieceGen destinationGen positionsGen statusGen 
    |> Arb.fromGen 
    |> Prop.forAll 
    <| fun (piece , destination , positions , status) -> (positions, status) 
                 |> move piece destination 
                 |> getPositions 
                 |> List.length = positions.Length 

付録:

はここに私のテストです

(* Types *) 
type Black = BlackKing | BlackSoldier 
type Red = RedKing | RedSoldier 

type Coordinate = int * int 

type Piece = 
    | Black of Black * Coordinate 
    | Red of Red * Coordinate 

type Space = 
    | Occupied of Piece 
    | Available of Coordinate 

type Status = 
    | BlacksTurn | RedsTurn 
    | BlackWins | RedWins 

(* Private *) 
let private black coordinate = Occupied (Black (BlackSoldier , coordinate)) 
let private red coordinate = Occupied (Red (RedSoldier , coordinate)) 

let private getPositions (positions:Space list, status:Status) = positions 

let private yDirection = function 
    | Black _ -> -1 
    | Red _ -> 1 

let private toAvailable = function 
    | Available pos -> true 
    | _    -> false 

let private available positions = positions |> List.filter toAvailable 

let private availableSelection = function 
    | Available pos -> Some pos 
    | Occupied _ -> None 

let private availablePositions positions = 
    positions |> List.filter toAvailable 
       |> List.choose availableSelection 

let private getCoordinate = function 
    | Available xy -> Some xy 
    | _   -> None 

let private coordinateOf = function 
    | Black (checker , pos) -> pos 
    | Red (checker , pos) -> pos 

let private optionsForSoldier piece = 

    let (sourceX , sourceY) = coordinateOf piece 

    (fun pos -> pos = ((sourceX - 1) , (sourceY + (piece |> yDirection))) || 
       pos = ((sourceX + 1) , (sourceY + (piece |> yDirection)))) 

let private optionsForKing piece = 

    let (sourceX , sourceY) = coordinateOf piece 

    (fun pos -> pos = ((sourceX - 1) , (sourceY + 1)) || 
       pos = ((sourceX + 1) , (sourceY + 1)) || 
       pos = ((sourceX - 1) , (sourceY - 1)) || 
       pos = ((sourceX + 1) , (sourceY - 1))) 

let private jumpOptions (sourceX , sourceY) space = 
    match space with 
    | Occupied p -> match p with 
        | Red (ch,xy) -> xy = (sourceX + 1, sourceY - 1) || 
             xy = (sourceX - 1, sourceY - 1) 

        | Black (ch,xy) -> xy = (sourceX + 1, sourceY + 1) || 
             xy = (sourceX - 1, sourceY + 1) 
    | _ -> false 

let private jumpsForSoldier piece positions = 
    match piece with 
    | Black (ch,pos) -> positions |> List.filter (jumpOptions (coordinateOf piece)) 
    | Red (ch,pos) -> positions |> List.filter (jumpOptions (coordinateOf piece)) 

let private isKing piece = 
    match piece with 
    | Black (checker , _) -> match checker with 
          | BlackSoldier -> false 
          | BlackKing -> true 

    | Red (checker , _) -> match checker with 
          | RedSoldier -> false 
          | RedKing  -> true 

let private filterOut a b positions = 
    positions |> List.filter(fun x -> x <> a && x <> b) 

let private movePiece destination positions piece = 

    let destinationXY = 
     match destination with 
     | Available xy -> xy 
     | Occupied p -> coordinateOf p 

    let yValueMin , yValueMax = 0 , 7 

    let canCrown = 
     let yValue = snd destinationXY 
     (yValue = yValueMin || 
     yValue = yValueMax) && 
     not (isKing piece) 

    match positions |> List.find (fun space -> space = Occupied piece) with 
    | Occupied (Black (ch, xy)) -> 
     let checkerType = if canCrown then BlackKing else BlackSoldier 
     Available(xy) :: (Occupied(Black(checkerType, destinationXY))) 
         :: (positions |> filterOut (Occupied (Black(ch, xy))) destination)  

    | Occupied (Red (ch, xy)) -> 
     let checkerType = if canCrown then RedKing else RedSoldier 
     Available(xy) :: (Occupied(Red(checkerType, destinationXY))) 
         :: (positions |> filterOut (Occupied (Red(ch, xy))) destination) 
    | _ -> positions 

(* Public *) 
let startGame() = 
    [ red (0,0); red (2,0); red (4,0); red (6,0) 
     red (1,1); red (3,1); red (5,1); red (7,1) 
     red (0,2); red (2,2); red (4,2); red (6,2) 

     Available (1,3); Available (3,3); Available (5,3); Available (7,3) 
     Available (0,4); Available (2,4); Available (4,4); Available (6,4) 

     black (1,5); black (3,5); black (5,5); black (7,5) 
     black (0,6); black (2,6); black (4,6); black (6,6) 
     black (1,7); black (3,7); black (5,7); black (7,7) ] , BlacksTurn 

let optionsFor piece positions = 

    let sourceX , sourceY = coordinateOf piece 

    match piece |> isKing with 
    | false -> positions |> availablePositions 
         |> List.filter (optionsForSoldier piece) 

    | true -> positions |> availablePositions 
         |> List.filter (optionsForKing piece) 

let move piece destination (positions,status) = 

    let currentStatus = match status with 
         | BlacksTurn -> RedsTurn 
         | RedsTurn -> BlacksTurn 
         | BlackWins -> BlackWins 
         | RedWins -> RedWins 

    let canProceed = match piece with 
         | Red _ -> currentStatus = RedsTurn 
         | Black _ -> currentStatus = BlacksTurn 

    if not canProceed then (positions , currentStatus) 
    else let options = optionsFor piece positions 
     let canMoveTo = (fun target -> options |> List.exists (fun xy -> xy = target)) 

     match getCoordinate destination with 
     | Some target -> if canMoveTo target then 
          let updatedBoard = ((positions , piece) ||> movePiece destination) 
          (updatedBoard , currentStatus) 

          else (positions , currentStatus) 
     | None -> (positions , currentStatus) 

let jump target positions source = 

    let canJump = 
     positions |> jumpsForSoldier source 
        |> List.exists (fun s -> match s with 
              | Occupied target -> true 
              | _    -> false) 

    let (|NorthEast|NorthWest|SouthEast|SouthWest|Origin|) (origin , barrier) = 

     let (sourceX , sourceY) = origin 
     let (barrierX , barrierY) = barrier 

     if barrierY = sourceY + 1 && 
      barrierX = sourceX - 1 
     then SouthWest 

     elif barrierY = sourceY + 1 && 
      barrierX = sourceX + 1 
     then SouthEast 

     elif barrierY = sourceY - 1 && 
      barrierX = sourceX - 1 
     then NorthWest 

     elif barrierY = sourceY - 1 && 
      barrierX = sourceX + 1 
     then NorthEast 

     else Origin 

    let jumpToPostion origin barrier = 

     let (sourceX , sourceY) = origin 
     let (barrierX , barrierY) = barrier 

     match (origin , barrier) with 
     | SouthWest -> (barrierX + 1, barrierY - 1) 
     | SouthEast -> (barrierX + 1, barrierY + 1) 
     | NorthWest -> (barrierX - 1, barrierY - 1) 
     | NorthEast -> (barrierX - 1, barrierY + 1) 
     | Origin -> origin 

    if canJump then 
     let destination = Available (jumpToPostion (coordinateOf source) (coordinateOf target)) 
     let result = (positions, source) ||> movePiece destination 
              |> List.filter (fun s -> s <> Occupied target) 
     Available (coordinateOf target)::result 
    else positions 

答えて

5

としてはa previous answerに説明し、あなたがより複雑な発電機を表現するためにgenの計算式を使用することができます。

この特定の例では、positionsGenには、pieceGenspaceGenによって生成された値を含める必要があります。

[<Property(QuietOnSuccess = true, MaxTest=10000)>] 
let ``moving checker retains set count``() = 
    gen { 
     let! piece = Arb.generate<Piece> 
     let! destination = Arb.generate<Space> 

     let! otherPositions = Arb.generate<Space list> 
     let! positions = 
      Occupied piece :: destination :: otherPositions |> Gen.shuffle 

     let! status = Arb.generate<Status> 
     return piece, destination, positions |> Array.toList, status } 
    |> Arb.fromGen 
    |> Prop.forAll 
    // ... the rest of the test goes here... 

計算式はpiecedestinationを生成することによって開始:あなたはこのようなことを行うことができます。計算式内のlet!の使用のために、内の内容はであり、通常の値はPieceSpaceであり、そのように扱うことができます。 (;生成されたリストが空である可能性がある場合)

次に、式は他の値を含むであろうSpace list値を、「生成」するlet!を使用します。

これは、少なくとも2つの望ましい値と他の値を含むリストを生成するために必要なすべてのビルディングブロックを提供します。そのようなリストを作成するには、2つの「既知の」値をリストに入れた後()、その結果をシャッフルしてよい尺度にします。

gen計算式の最後の式は、4要素タプルを返します。その式のタイプはGen<Piece * Space * Space list * Status>です。それはにArb.fromGenに変えられ、さらにProp.forAllにパイプされます。

これは、moving checker retains set countプロパティが例外を内部的にスローする問題を解決します。


これは、偶然に、プロパティが反証であることを示しています

Test 'Ploeh.StackOverflow.Q38857462.Properties.moving checker retains set count' failed: FsCheck.Xunit.PropertyFailedException : 
Falsifiable, after 70 tests (0 shrinks) (StdGen (1318556550,296190265)): 
Original: 
<null> 
(Black (BlackKing,(-1, 1)), Available (0, 0), 
[Occupied (Red (RedSoldier,(-1, 0))); Available (0, 0); 
    Occupied (Black (BlackKing,(-1, 1))); Available (0, 0)], RedsTurn) 

これはテストとや実装が異なる問題であるとの問題があるかどうか...

+0

このプロパティベースのテストは、同時に厄介で恐ろしいものです。 FsCheckは私が知りませんでした私のロジックのギャップを見つけることです。どうもありがとうございます! –

+2

@ScottNimrodあなたはある時点で迷惑を乗り越えることを願っています - 私はそれが素晴らしいことを単に見つける:) –

関連する問題