Skip to main content

tile_system_demo/
tile_system_demo.rs

1/**
2 * @file tile_system_demo.rs
3 * @author amazcuter (amazcuter@outlook.com)
4 * @brief 瓷砖系统使用示例
5 *        展示TileSet和TileSetVirtual trait的使用,包括不同类型的瓷砖约束实现
6 * @version 1.0
7 * @date 2025-01-25
8 *
9 * @copyright Copyright (c) 2025
10 */
11use rlwfc::{GridError, Tile, TileId, TileSet, TileSetVirtual};
12
13// =============================================================================
14// 简单瓷砖集实现 - 字符串边数据
15// =============================================================================
16
17/// 简单瓷砖集,使用字符串作为边数据
18///
19/// 这是最基本的瓷砖集实现,适合演示WFC的基本概念。
20/// 每个瓷砖有四个边,用字符串表示边的类型。
21struct SimpleTileSet {
22    tiles: TileSet<&'static str>,
23}
24
25impl SimpleTileSet {
26    fn new() -> Self {
27        Self {
28            tiles: TileSet::new(),
29        }
30    }
31
32    fn get_all_tiles(&self) -> &[Tile<&'static str>] {
33        self.tiles.get_all_tiles()
34    }
35}
36
37impl TileSetVirtual<&'static str> for SimpleTileSet {
38    fn build_tile_set(&mut self) -> Result<(), GridError> {
39        // 构建基础瓷砖
40        self.tiles.clear();
41
42        // 添加全草地瓷砖
43        self.tiles
44            .add_tile(vec!["grass", "grass", "grass", "grass"], 50);
45
46        // 添加全水面瓷砖
47        self.tiles
48            .add_tile(vec!["water", "water", "water", "water"], 30);
49
50        // 添加边界瓷砖
51        self.tiles
52            .add_tile(vec!["grass", "water", "grass", "water"], 20);
53        self.tiles
54            .add_tile(vec!["water", "grass", "water", "grass"], 20);
55
56        println!("构建完成:添加了 {} 个瓷砖", self.tiles.get_tile_count());
57        Ok(())
58    }
59
60    fn judge_possibility(&self, neighbor_possibilities: &[Vec<TileId>], candidate: TileId) -> bool {
61        // 简单的兼容性检查:如果有邻居约束,检查候选瓷砖是否存在
62        if neighbor_possibilities.is_empty() {
63            return true;
64        }
65
66        // 检查候选瓷砖是否存在
67        self.tiles.get_tile(candidate).is_some()
68    }
69
70    fn get_tile(&self, tile_id: TileId) -> Option<&Tile<&'static str>> {
71        self.tiles.get_tile(tile_id)
72    }
73
74    fn get_tile_count(&self) -> usize {
75        self.tiles.get_tile_count()
76    }
77
78    fn get_all_tile_ids(&self) -> Vec<TileId> {
79        self.tiles.get_all_tile_ids()
80    }
81}
82
83// =============================================================================
84// 数字瓷砖集实现 - 数字边数据
85// =============================================================================
86
87/// 数字瓷砖集,使用整数作为边数据
88///
89/// 展示如何使用数字来表示边的类型,适合更复杂的约束计算。
90struct NumericTileSet {
91    tiles: TileSet<i32>,
92}
93
94impl NumericTileSet {
95    fn new() -> Self {
96        Self {
97            tiles: TileSet::new(),
98        }
99    }
100}
101
102impl TileSetVirtual<i32> for NumericTileSet {
103    fn build_tile_set(&mut self) -> Result<(), GridError> {
104        // 构建数值瓷砖
105        self.tiles.clear();
106
107        // 添加基础连接模式
108        self.tiles.add_tile(vec![1, 1, 1, 1], 40); // 全连接
109        self.tiles.add_tile(vec![0, 0, 0, 0], 30); // 全断开
110        self.tiles.add_tile(vec![1, 0, 1, 0], 20); // 上下连接
111        self.tiles.add_tile(vec![0, 1, 0, 1], 20); // 左右连接
112
113        println!("数值瓷砖集构建完成:{} 个瓷砖", self.tiles.get_tile_count());
114        Ok(())
115    }
116
117    fn judge_possibility(&self, neighbor_possibilities: &[Vec<TileId>], candidate: TileId) -> bool {
118        let Some(candidate_tile) = self.tiles.get_tile(candidate) else {
119            return false;
120        };
121
122        for (direction, neighbor_ids) in neighbor_possibilities.iter().enumerate() {
123            if neighbor_ids.is_empty() {
124                continue;
125            }
126
127            let is_compatible = neighbor_ids.iter().any(|&neighbor_id| {
128                if let Some(neighbor_tile) = self.tiles.get_tile(neighbor_id) {
129                    let opposite_direction = (direction + 2) % 4;
130                    if let (Some(&current_edge), Some(&neighbor_edge)) = (
131                        candidate_tile.get_edge(direction),
132                        neighbor_tile.get_edge(opposite_direction),
133                    ) {
134                        // 数字边需要完全匹配
135                        current_edge == neighbor_edge
136                    } else {
137                        false
138                    }
139                } else {
140                    false
141                }
142            });
143
144            if !is_compatible {
145                return false;
146            }
147        }
148
149        true
150    }
151
152    fn get_tile(&self, tile_id: TileId) -> Option<&Tile<i32>> {
153        self.tiles.get_tile(tile_id)
154    }
155
156    fn get_tile_count(&self) -> usize {
157        self.tiles.get_tile_count()
158    }
159
160    fn get_all_tile_ids(&self) -> Vec<TileId> {
161        self.tiles.get_all_tile_ids()
162    }
163}
164
165// =============================================================================
166// 主函数 - 演示所有瓷砖集
167// =============================================================================
168
169fn main() -> Result<(), Box<dyn std::error::Error>> {
170    println!("=== 瓷砖系统综合示例 ===\n");
171
172    // 1. 基础TileSet演示
173    println!("1. 基础TileSet使用:");
174    demonstrate_basic_tileset()?;
175
176    // 2. 简单瓷砖集演示
177    println!("\n2. 简单瓷砖集 (字符串边):");
178    demonstrate_simple_tileset()?;
179
180    // 3. 数字瓷砖集演示
181    println!("\n3. 数字瓷砖集 (整数边):");
182    demonstrate_numeric_tileset()?;
183
184    // 4. 约束判断演示
185    println!("\n4. 约束判断测试:");
186    demonstrate_constraint_checking()?;
187
188    println!("\n=== 示例完成 ===");
189    Ok(())
190}
191
192/// 演示基础TileSet的使用
193fn demonstrate_basic_tileset() -> Result<(), Box<dyn std::error::Error>> {
194    let mut tile_set = TileSet::new();
195
196    println!("   创建基础瓷砖集...");
197
198    // 添加一些基础瓷砖
199    let tile1 = tile_set.add_tile(vec!["A", "B", "C", "D"], 10);
200    let tile2 = tile_set.add_tile(vec!["B", "A", "D", "C"], 15);
201    let _tile3 = tile_set.add_tile(vec!["C", "D", "A", "B"], 5);
202
203    println!("   添加了 {} 个瓷砖", tile_set.get_tile_count());
204
205    // 展示瓷砖信息
206    for (i, tile) in tile_set.get_all_tiles().iter().enumerate() {
207        println!(
208            "   瓷砖 {}: ID={}, 权重={}, 边={:?}",
209            i, tile.id, tile.weight, tile.edges
210        );
211    }
212
213    // 测试瓷砖兼容性
214    if let (Some(t1), Some(t2)) = (tile_set.get_tile(tile1), tile_set.get_tile(tile2)) {
215        let compatible = t1.is_compatible_with(t2, 0); // 方向0
216        println!("   瓷砖0和瓷砖1在方向0兼容: {}", compatible);
217    }
218
219    Ok(())
220}
221
222/// 演示简单瓷砖集
223fn demonstrate_simple_tileset() -> Result<(), Box<dyn std::error::Error>> {
224    let mut simple_tileset = SimpleTileSet::new();
225
226    // 构建瓷砖集
227    simple_tileset.build_tile_set()?;
228
229    // 展示所有瓷砖
230    println!("\n   瓷砖详情:");
231    for (i, tile) in simple_tileset.get_all_tiles().iter().enumerate() {
232        println!("     瓷砖 {}: 边={:?}, 权重={}", i, tile.edges, tile.weight);
233    }
234
235    // 测试约束判断
236    println!("\n   约束判断测试:");
237    test_constraint_judgment(&simple_tileset);
238
239    Ok(())
240}
241
242/// 演示数字瓷砖集
243fn demonstrate_numeric_tileset() -> Result<(), Box<dyn std::error::Error>> {
244    let mut numeric_tileset = NumericTileSet::new();
245
246    // 构建瓷砖集
247    numeric_tileset.build_tile_set()?;
248
249    // 展示瓷砖统计
250    println!("\n   瓷砖统计:");
251    let mut edge_types = std::collections::HashMap::new();
252    for tile in numeric_tileset.tiles.get_all_tiles() {
253        for &edge in &tile.edges {
254            *edge_types.entry(edge).or_insert(0) += 1;
255        }
256    }
257
258    for (edge_type, count) in edge_types {
259        let type_name = match edge_type {
260            0 => "平原",
261            1 => "山脉",
262            2 => "河流",
263            _ => "未知",
264        };
265        println!("     边类型 {} ({}): {} 个边", edge_type, type_name, count);
266    }
267
268    Ok(())
269}
270
271/// 演示约束判断
272fn demonstrate_constraint_checking() -> Result<(), Box<dyn std::error::Error>> {
273    let mut tileset = SimpleTileSet::new();
274    tileset.build_tile_set()?;
275
276    println!("   测试不同约束情况:");
277
278    // 情况1: 无约束
279    let no_constraints: Vec<Vec<TileId>> = vec![];
280    let result1 = tileset.judge_possibility(&no_constraints, 0);
281    println!(
282        "     无约束情况: {}",
283        if result1 { "可放置" } else { "不可放置" }
284    );
285
286    // 情况2: 单方向约束
287    let single_constraint = vec![vec![0], vec![], vec![], vec![]]; // 只有东向有约束
288    let result2 = tileset.judge_possibility(&single_constraint, 1);
289    println!(
290        "     单方向约束: {}",
291        if result2 { "可放置" } else { "不可放置" }
292    );
293
294    // 情况3: 多方向约束
295    let multi_constraints = vec![vec![0], vec![1], vec![], vec![]]; // 东向和南向都有约束
296    let result3 = tileset.judge_possibility(&multi_constraints, 2);
297    println!(
298        "     多方向约束: {}",
299        if result3 { "可放置" } else { "不可放置" }
300    );
301
302    // 情况4: 不存在的瓷砖
303    let result4 = tileset.judge_possibility(&single_constraint, 999);
304    println!(
305        "     不存在瓷砖: {}",
306        if result4 { "可放置" } else { "不可放置" }
307    );
308
309    Ok(())
310}
311
312/// 测试约束判断逻辑
313fn test_constraint_judgment(tileset: &SimpleTileSet) {
314    // 创建测试场景:两个相邻瓷砖
315    let grass_tile_id = 0; // 草地瓷砖
316    let water_tile_id = 1; // 水面瓷砖
317
318    // 测试相同类型瓷砖的兼容性
319    let same_type_constraint = vec![vec![grass_tile_id], vec![], vec![], vec![]];
320    let compatible_with_same = tileset.judge_possibility(&same_type_constraint, grass_tile_id);
321    println!("     草地瓷砖与草地瓷砖兼容: {}", compatible_with_same);
322
323    // 测试不同类型瓷砖的兼容性
324    let diff_type_constraint = vec![vec![water_tile_id], vec![], vec![], vec![]];
325    let compatible_with_diff = tileset.judge_possibility(&diff_type_constraint, grass_tile_id);
326    println!("     草地瓷砖与水面瓷砖兼容: {}", compatible_with_diff);
327
328    // 测试过渡瓷砖的兼容性
329    if let Some(transition_tile) = tileset.get_tile(2) {
330        println!("     过渡瓷砖边: {:?}", transition_tile.edges);
331        let transition_compatible = tileset.judge_possibility(&same_type_constraint, 2);
332        println!("     过渡瓷砖与草地瓷砖兼容: {}", transition_compatible);
333    }
334}